diff options
Diffstat (limited to 'src')
592 files changed, 19005 insertions, 10314 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock index 4c6aeeddd38..9cd77e71b82 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -19,7 +19,7 @@ version = "0.0.0" dependencies = [ "build_helper 0.1.0", "core 0.0.0", - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.0.0", ] @@ -42,7 +42,7 @@ dependencies = [ "build_helper 0.1.0", "cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -63,7 +63,7 @@ name = "cmake" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -80,7 +80,7 @@ name = "compiler_builtins" version = "0.0.0" dependencies = [ "core 0.0.0", - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -121,7 +121,7 @@ name = "flate" version = "0.0.0" dependencies = [ "build_helper 0.1.0", - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -130,7 +130,7 @@ version = "0.0.0" [[package]] name = "gcc" -version = "0.3.38" +version = "0.3.40" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -402,7 +402,7 @@ name = "rustc_llvm" version = "0.0.0" dependencies = [ "build_helper 0.1.0", - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_bitflags 0.0.0", ] @@ -551,7 +551,7 @@ version = "0.0.0" dependencies = [ "arena 0.0.0", "build_helper 0.1.0", - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.0.0", "rustc 0.0.0", "rustc_back 0.0.0", @@ -587,7 +587,7 @@ dependencies = [ "collections 0.0.0", "compiler_builtins 0.0.0", "core 0.0.0", - "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.0.0", "panic_abort 0.0.0", "panic_unwind 0.0.0", @@ -677,7 +677,7 @@ dependencies = [ "checksum cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0e5bcf27e097a184c1df4437654ed98df3d7a516e8508a6ba45d8b092bbdf283" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922" -"checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5" +"checksum gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "872db9e59486ef2b14f8e8c10e9ef02de2bccef6363d7f34835dedb386b3d950" "checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685" "checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index d0b501e4d89..ac84edb4038 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -22,7 +22,7 @@ Note that if you're on Unix you should be able to execute the script directly: ./x.py build ``` -The script accepts commands, flags, and filters to determine what to do: +The script accepts commands, flags, and arguments to determine what to do: * `build` - a general purpose command for compiling code. Alone `build` will bootstrap the entire compiler, and otherwise arguments passed indicate what to @@ -42,6 +42,15 @@ The script accepts commands, flags, and filters to determine what to do: ./x.py build --stage 0 src/libtest ``` + If files are dirty that would normally be rebuilt from stage 0, that can be + overidden using `--keep-stage 0`. Using `--keep-stage n` will skip all steps + that belong to stage n or earlier: + + ``` + # keep old build products for stage 0 and build stage 1 + ./x.py build --keep-stage 0 --stage 1 + ``` + * `test` - a command for executing unit tests. Like the `build` command this will execute the entire test suite by default, and otherwise it can be used to select which test suite is run: @@ -54,7 +63,7 @@ The script accepts commands, flags, and filters to determine what to do: ./x.py test src/test/run-pass # execute only some tests in the run-pass test suite - ./x.py test src/test/run-pass --filter my-filter + ./x.py test src/test/run-pass --test-args substring-of-test-name # execute tests in the standard library in stage0 ./x.py test --stage 0 src/libstd @@ -107,6 +116,42 @@ compiler. What actually happens when you invoke rustbuild is: The goal of each stage is to (a) leverage Cargo as much as possible and failing that (b) leverage Rust as much as possible! +## Incremental builds + +You can configure rustbuild to use incremental compilation. Because +incremental is new and evolving rapidly, if you want to use it, it is +recommended that you replace the snapshot with a locally installed +nightly build of rustc. You will want to keep this up to date. + +To follow this course of action, first thing you will want to do is to +install a nightly, presumably using `rustup`. You will then want to +configure your directory to use this build, like so: + +``` +# configure to use local rust instead of downloding a beta. +# `--local-rust-root` is optional here. If elided, we will +# use whatever rustc we find on your PATH. +> configure --enable-rustbuild --local-rust-root=~/.cargo/ --enable-local-rebuild +``` + +After that, you can use the `--incremental` flag to actually do +incremental builds: + +``` +> ../x.py build --incremental +``` + +The `--incremental` flag will store incremental compilation artifacts +in `build/<host>/stage0-incremental`. Note that we only use incremental +compilation for the stage0 -> stage1 compilation -- this is because +the stage1 compiler is changing, and we don't try to cache and reuse +incremental artifacts across different versions of the compiler. For +this reason, `--incremental` defaults to `--stage 1` (though you can +manually select a higher stage, if you prefer). + +You can always drop the `--incremental` to build as normal (but you +will still be using the local nightly as your bootstrap). + ## Directory Layout This build system houses all output under the `build` directory, which looks diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 2f674a311fe..9cab6c423f5 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -25,12 +25,17 @@ //! switching compilers for the bootstrap and for build scripts will probably //! never get replaced. +#![deny(warnings)] + extern crate bootstrap; use std::env; use std::ffi::OsString; +use std::io; +use std::io::prelude::*; +use std::str::FromStr; use std::path::PathBuf; -use std::process::Command; +use std::process::{Command, ExitStatus}; fn main() { let args = env::args_os().skip(1).collect::<Vec<_>>(); @@ -41,6 +46,11 @@ fn main() { .and_then(|w| w[1].to_str()); let version = args.iter().find(|w| &**w == "-vV"); + let verbose = match env::var("RUSTC_VERBOSE") { + Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"), + Err(_) => 0, + }; + // Build scripts always use the snapshot compiler which is guaranteed to be // able to produce an executable, whereas intermediate compilers may not // have the standard library built yet and may not be able to produce an @@ -95,6 +105,15 @@ fn main() { cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>()); } + // Pass down incremental directory, if any. + if let Ok(dir) = env::var("RUSTC_INCREMENTAL") { + cmd.arg(format!("-Zincremental={}", dir)); + + if verbose > 0 { + cmd.arg("-Zincremental-info"); + } + } + // If we're compiling specifically the `panic_abort` crate then we pass // the `-C panic=abort` option. Note that we do not do this for any // other crate intentionally as this is the only crate for now that we @@ -158,6 +177,15 @@ fn main() { // to change a flag in a binary? if env::var("RUSTC_RPATH") == Ok("true".to_string()) { let rpath = if target.contains("apple") { + + // Note that we need to take one extra step on OSX to also pass + // `-Wl,-instal_name,@rpath/...` to get things to work right. To + // do that we pass a weird flag to the compiler to get it to do + // so. Note that this is definitely a hack, and we should likely + // flesh out rpath support more fully in the future. + if stage != "0" { + cmd.arg("-Z").arg("osx-rpath-install-name"); + } Some("-Wl,-rpath,@loader_path/../lib") } else if !target.contains("windows") { Some("-Wl,-rpath,$ORIGIN/../lib") @@ -167,12 +195,33 @@ fn main() { if let Some(rpath) = rpath { cmd.arg("-C").arg(format!("link-args={}", rpath)); } + + if let Ok(s) = env::var("RUSTFLAGS") { + for flag in s.split_whitespace() { + cmd.arg(flag); + } + } } } + if verbose > 1 { + writeln!(&mut io::stderr(), "rustc command: {:?}", cmd).unwrap(); + } + // Actually run the compiler! - std::process::exit(match cmd.status() { - Ok(s) => s.code().unwrap_or(1), + std::process::exit(match exec_cmd(&mut cmd) { + Ok(s) => s.code().unwrap_or(0xfe), Err(e) => panic!("\n\nfailed to run {:?}: {}\n\n", cmd, e), }) } + +#[cfg(unix)] +fn exec_cmd(cmd: &mut Command) -> ::std::io::Result<ExitStatus> { + use std::os::unix::process::CommandExt; + Err(cmd.exec()) +} + +#[cfg(not(unix))] +fn exec_cmd(cmd: &mut Command) -> ::std::io::Result<ExitStatus> { + cmd.status() +} diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index 67358e540da..a53bbe22eb9 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -12,6 +12,8 @@ //! //! See comments in `src/bootstrap/rustc.rs` for more information. +#![deny(warnings)] + extern crate bootstrap; use std::env; diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 0dda7f12007..89d297760e2 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -81,7 +81,7 @@ def verify(path, sha_path, verbose): with open(path, "rb") as f: found = hashlib.sha256(f.read()).hexdigest() with open(sha_path, "r") as f: - expected, _ = f.readline().split() + expected = f.readline().split()[0] verified = found == expected if not verified: print("invalid checksum:\n" @@ -146,7 +146,7 @@ class RustBuild(object): def download_stage0(self): cache_dst = os.path.join(self.build_dir, "cache") rustc_cache = os.path.join(cache_dst, self.stage0_rustc_date()) - cargo_cache = os.path.join(cache_dst, self.stage0_cargo_date()) + cargo_cache = os.path.join(cache_dst, self.stage0_cargo_rev()) if not os.path.exists(rustc_cache): os.makedirs(rustc_cache) if not os.path.exists(cargo_cache): @@ -179,21 +179,17 @@ class RustBuild(object): if self.cargo().startswith(self.bin_root()) and \ (not os.path.exists(self.cargo()) or self.cargo_out_of_date()): self.print_what_it_means_to_bootstrap() - channel = self.stage0_cargo_channel() - filename = "cargo-{}-{}.tar.gz".format(channel, self.build) - url = "https://static.rust-lang.org/cargo-dist/" + self.stage0_cargo_date() + filename = "cargo-nightly-{}.tar.gz".format(self.build) + url = "https://s3.amazonaws.com/rust-lang-ci/cargo-builds/" + self.stage0_cargo_rev() tarball = os.path.join(cargo_cache, filename) if not os.path.exists(tarball): get("{}/{}".format(url, filename), tarball, verbose=self.verbose) unpack(tarball, self.bin_root(), match="cargo", verbose=self.verbose) with open(self.cargo_stamp(), 'w') as f: - f.write(self.stage0_cargo_date()) + f.write(self.stage0_cargo_rev()) - def stage0_cargo_date(self): - return self._cargo_date - - def stage0_cargo_channel(self): - return self._cargo_channel + def stage0_cargo_rev(self): + return self._cargo_rev def stage0_rustc_date(self): return self._rustc_date @@ -217,7 +213,7 @@ class RustBuild(object): if not os.path.exists(self.cargo_stamp()) or self.clean: return True with open(self.cargo_stamp(), 'r') as f: - return self.stage0_cargo_date() != f.read() + return self.stage0_cargo_rev() != f.read() def bin_root(self): return os.path.join(self.build_dir, self.build, "stage0") @@ -294,6 +290,8 @@ class RustBuild(object): env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") env["PATH"] = os.path.join(self.bin_root(), "bin") + \ os.pathsep + env["PATH"] + if not os.path.isfile(self.cargo()): + raise Exception("no cargo executable found at `%s`" % self.cargo()) args = [self.cargo(), "build", "--manifest-path", os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")] if self.use_vendored_sources: @@ -467,7 +465,7 @@ def main(): data = stage0_data(rb.rust_root) rb._rustc_channel, rb._rustc_date = data['rustc'].split('-', 1) - rb._cargo_channel, rb._cargo_date = data['cargo'].split('-', 1) + rb._cargo_rev = data['cargo'] start_time = time() diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index b2341f59787..c38bb33aa02 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -15,7 +15,7 @@ //! `package_vers`, and otherwise indicating to the compiler what it should //! print out as part of its version information. -use std::fs::{self, File}; +use std::fs::File; use std::io::prelude::*; use std::process::Command; @@ -69,7 +69,7 @@ pub fn collect(build: &mut Build) { // If we have a git directory, add in some various SHA information of what // commit this compiler was compiled from. - if fs::metadata(build.src.join(".git")).is_ok() { + if build.src.join(".git").is_dir() { let ver_date = output(Command::new("git").current_dir(&build.src) .arg("log").arg("-1") .arg("--date=short") diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index e0798860275..e7b0afeb8ce 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -13,6 +13,8 @@ //! This file implements the various regression test suites that we execute on //! our CI. +extern crate build_helper; + use std::collections::HashSet; use std::env; use std::fmt; @@ -190,7 +192,7 @@ pub fn compiletest(build: &Build, cmd.args(&build.flags.cmd.test_args()); - if build.config.verbose || build.flags.verbose { + if build.config.verbose() || build.flags.verbose() { cmd.arg("--verbose"); } @@ -299,6 +301,7 @@ fn markdown_test(build: &Build, compiler: &Compiler, markdown: &Path) { build.add_rust_test_threads(&mut cmd); cmd.arg("--test"); cmd.arg(markdown); + cmd.env("RUSTC_BOOTSTRAP", "1"); let mut test_args = build.flags.cmd.test_args().join(" "); if build.config.quiet_tests { @@ -542,7 +545,7 @@ pub fn distcheck(build: &Build) { build.run(&mut cmd); build.run(Command::new("./configure") .current_dir(&dir)); - build.run(Command::new("make") + build.run(Command::new(build_helper::make(&build.config.build)) .arg("check") .current_dir(&dir)); } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 60f65f62300..6b86e537b7d 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -38,9 +38,9 @@ use util::push_exe_path; /// `src/bootstrap/config.toml.example`. #[derive(Default)] pub struct Config { - pub ccache: bool, + pub ccache: Option<String>, pub ninja: bool, - pub verbose: bool, + pub verbose: usize, pub submodules: bool, pub compiler_docs: bool, pub docs: bool, @@ -113,6 +113,7 @@ pub struct Target { #[derive(RustcDecodable, Default)] struct TomlConfig { build: Option<Build>, + install: Option<Install>, llvm: Option<Llvm>, rust: Option<Rust>, target: Option<HashMap<String, TomlTarget>>, @@ -135,10 +136,16 @@ struct Build { python: Option<String>, } +/// TOML representation of various global install decisions. +#[derive(RustcDecodable, Default, Clone)] +struct Install { + prefix: Option<String>, +} + /// TOML representation of how the LLVM build is configured. #[derive(RustcDecodable, Default)] struct Llvm { - ccache: Option<bool>, + ccache: Option<StringOrBool>, ninja: Option<bool>, assertions: Option<bool>, optimize: Option<bool>, @@ -147,6 +154,18 @@ struct Llvm { static_libstdcpp: Option<bool>, } +#[derive(RustcDecodable)] +enum StringOrBool { + String(String), + Bool(bool), +} + +impl Default for StringOrBool { + fn default() -> StringOrBool { + StringOrBool::Bool(false) + } +} + /// TOML representation of how the Rust build is configured. #[derive(RustcDecodable, Default)] struct Rust { @@ -246,8 +265,20 @@ impl Config { set(&mut config.submodules, build.submodules); set(&mut config.vendor, build.vendor); + if let Some(ref install) = toml.install { + config.prefix = install.prefix.clone(); + } + if let Some(ref llvm) = toml.llvm { - set(&mut config.ccache, llvm.ccache); + match llvm.ccache { + Some(StringOrBool::String(ref s)) => { + config.ccache = Some(s.to_string()) + } + Some(StringOrBool::Bool(true)) => { + config.ccache = Some("ccache".to_string()); + } + Some(StringOrBool::Bool(false)) | None => {} + } set(&mut config.ninja, llvm.ninja); set(&mut config.llvm_assertions, llvm.assertions); set(&mut config.llvm_optimize, llvm.optimize); @@ -255,6 +286,7 @@ impl Config { set(&mut config.llvm_version_check, llvm.version_check); set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp); } + if let Some(ref rust) = toml.rust { set(&mut config.rust_debug_assertions, rust.debug_assertions); set(&mut config.rust_debuginfo, rust.debuginfo); @@ -338,7 +370,6 @@ impl Config { } check! { - ("CCACHE", self.ccache), ("MANAGE_SUBMODULES", self.submodules), ("COMPILER_DOCS", self.compiler_docs), ("DOCS", self.docs), @@ -475,10 +506,24 @@ impl Config { let path = parse_configure_path(value); self.python = Some(path); } + "CFG_ENABLE_CCACHE" if value == "1" => { + self.ccache = Some("ccache".to_string()); + } + "CFG_ENABLE_SCCACHE" if value == "1" => { + self.ccache = Some("sccache".to_string()); + } _ => {} } } } + + pub fn verbose(&self) -> bool { + self.verbose > 0 + } + + pub fn very_verbose(&self) -> bool { + self.verbose > 1 + } } #[cfg(not(windows))] diff --git a/src/bootstrap/config.toml.example b/src/bootstrap/config.toml.example index b6774b3af20..5fc095137c7 100644 --- a/src/bootstrap/config.toml.example +++ b/src/bootstrap/config.toml.example @@ -25,6 +25,8 @@ # Indicates whether ccache is used when building LLVM #ccache = false +# or alternatively ... +#ccache = "/path/to/ccache" # If an external LLVM root is specified, we automatically check the version by # default to make sure it's within the range that we're expecting, but setting @@ -99,6 +101,14 @@ #vendor = false # ============================================================================= +# General install configuration options +# ============================================================================= +[install] + +# Instead of installing to /usr/local, install to this path instead. +#prefix = "/path/to/install" + +# ============================================================================= # Options for compiling Rust code itself # ============================================================================= [rust] diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 1d3445a9eac..6e3174ed2f6 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -48,6 +48,11 @@ pub fn tmpdir(build: &Build) -> PathBuf { /// Slurps up documentation from the `stage`'s `host`. pub fn docs(build: &Build, stage: u32, host: &str) { println!("Dist docs stage{} ({})", stage, host); + if !build.config.docs { + println!("\tskipping - docs disabled"); + return + } + let name = format!("rust-docs-{}", package_vers(build)); let image = tmpdir(build).join(format!("{}-{}-image", name, name)); let _ = fs::remove_dir_all(&image); @@ -92,6 +97,7 @@ pub fn mingw(build: &Build, host: &str) { let name = format!("rust-mingw-{}", package_vers(build)); let image = tmpdir(build).join(format!("{}-{}-image", name, host)); let _ = fs::remove_dir_all(&image); + t!(fs::create_dir_all(&image)); // The first argument to the script is a "temporary directory" which is just // thrown away (this contains the runtime DLLs included in the rustc package @@ -260,6 +266,14 @@ pub fn debugger_scripts(build: &Build, pub fn std(build: &Build, compiler: &Compiler, target: &str) { println!("Dist std stage{} ({} -> {})", compiler.stage, compiler.host, target); + + // The only true set of target libraries came from the build triple, so + // let's reduce redundant work by only producing archives from that host. + if compiler.host != build.config.build { + println!("\tskipping, not a build host"); + return + } + let name = format!("rust-std-{}", package_vers(build)); let image = tmpdir(build).join(format!("{}-{}-image", name, target)); let _ = fs::remove_dir_all(&image); @@ -294,10 +308,15 @@ pub fn analysis(build: &Build, compiler: &Compiler, target: &str) { println!("Dist analysis"); if build.config.channel != "nightly" { - println!("Skipping dist-analysis - not on nightly channel"); + println!("\tskipping - not on nightly channel"); return; } + if compiler.host != build.config.build { + println!("\tskipping - not a build host"); + return + } if compiler.stage != 2 { + println!("\tskipping - not stage2"); return } @@ -324,18 +343,17 @@ pub fn analysis(build: &Build, compiler: &Compiler, target: &str) { .arg("--legacy-manifest-dirs=rustlib,cargo"); build.run(&mut cmd); t!(fs::remove_dir_all(&image)); - - // Create plain source tarball - let mut cmd = Command::new("tar"); - cmd.arg("-czf").arg(sanitize_sh(&distdir(build).join(&format!("{}.tar.gz", name)))) - .arg("analysis") - .current_dir(&src); - build.run(&mut cmd); } /// Creates the `rust-src` installer component and the plain source tarball -pub fn rust_src(build: &Build) { +pub fn rust_src(build: &Build, host: &str) { println!("Dist src"); + + if host != build.config.build { + println!("\tskipping, not a build host"); + return + } + let plain_name = format!("rustc-{}-src", package_vers(build)); let name = format!("rust-src-{}", package_vers(build)); let image = tmpdir(build).join(format!("{}-image", name)); diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 7a2d56fc5d3..b2412fbb3c8 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -27,8 +27,9 @@ use step; /// Deserialized version of all flags for this compile. pub struct Flags { - pub verbose: bool, + pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose pub stage: Option<u32>, + pub keep_stage: Option<u32>, pub build: String, pub host: Vec<String>, pub target: Vec<String>, @@ -36,6 +37,17 @@ pub struct Flags { pub src: Option<PathBuf>, pub jobs: Option<u32>, pub cmd: Subcommand, + pub incremental: bool, +} + +impl Flags { + pub fn verbose(&self) -> bool { + self.verbose > 0 + } + + pub fn very_verbose(&self) -> bool { + self.verbose > 1 + } } pub enum Subcommand { @@ -62,12 +74,14 @@ pub enum Subcommand { impl Flags { pub fn parse(args: &[String]) -> Flags { let mut opts = Options::new(); - opts.optflag("v", "verbose", "use verbose output"); + opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)"); + opts.optflag("i", "incremental", "use incremental compilation"); opts.optopt("", "config", "TOML configuration file for build", "FILE"); opts.optopt("", "build", "build target of the stage0 compiler", "BUILD"); opts.optmulti("", "host", "host targets to build", "HOST"); opts.optmulti("", "target", "target targets to build", "TARGET"); opts.optopt("", "stage", "stage to build", "N"); + opts.optopt("", "keep-stage", "stage to keep without recompiling", "N"); opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS"); opts.optflag("h", "help", "print this help message"); @@ -108,7 +122,6 @@ Arguments: tests that should be compiled and run. For example: ./x.py test src/test/run-pass - ./x.py test src/test/run-pass/assert-* ./x.py test src/libstd --test-args hash_map ./x.py test src/libstd --stage 0 @@ -255,9 +268,20 @@ To learn more about a subcommand, run `./x.py <command> -h` } }); + let mut stage = m.opt_str("stage").map(|j| j.parse().unwrap()); + + let incremental = m.opt_present("i"); + + if incremental { + if stage.is_none() { + stage = Some(1); + } + } + Flags { - verbose: m.opt_present("v"), - stage: m.opt_str("stage").map(|j| j.parse().unwrap()), + verbose: m.opt_count("v"), + stage: stage, + keep_stage: m.opt_str("keep-stage").map(|j| j.parse().unwrap()), build: m.opt_str("build").unwrap_or_else(|| { env::var("BUILD").unwrap() }), @@ -267,6 +291,7 @@ To learn more about a subcommand, run `./x.py <command> -h` src: m.opt_str("src").map(PathBuf::from), jobs: m.opt_str("jobs").map(|j| j.parse().unwrap()), cmd: cmd, + incremental: incremental, } } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index cd80c4298dc..665e0c67b7f 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -64,6 +64,8 @@ //! More documentation can be found in each respective module below, and you can //! also check out the `src/bootstrap/README.md` file for more information. +#![deny(warnings)] + extern crate build_helper; extern crate cmake; extern crate filetime; @@ -74,6 +76,7 @@ extern crate rustc_serialize; extern crate toml; use std::collections::HashMap; +use std::cmp; use std::env; use std::ffi::OsString; use std::fs::{self, File}; @@ -497,6 +500,17 @@ impl Build { cargo.env("RUSTC_BOOTSTRAP", "1"); self.add_rust_test_threads(&mut cargo); + // Ignore incremental modes except for stage0, since we're + // not guaranteeing correctness acros builds if the compiler + // is changing under your feet.` + if self.flags.incremental && compiler.stage == 0 { + let incr_dir = self.incremental_dir(compiler); + cargo.env("RUSTC_INCREMENTAL", incr_dir); + } + + let verbose = cmp::max(self.config.verbose, self.flags.verbose); + cargo.env("RUSTC_VERBOSE", format!("{}", verbose)); + // Specify some various options for build scripts used throughout // the build. // @@ -516,7 +530,7 @@ impl Build { // FIXME: should update code to not require this env var cargo.env("CFG_COMPILER_HOST_TRIPLE", target); - if self.config.verbose || self.flags.verbose { + if self.config.verbose() || self.flags.verbose() { cargo.arg("-v"); } // FIXME: cargo bench does not accept `--release` @@ -630,6 +644,12 @@ impl Build { } } + /// Get the directory for incremental by-products when using the + /// given compiler. + fn incremental_dir(&self, compiler: &Compiler) -> PathBuf { + self.out.join(compiler.host).join(format!("stage{}-incremental", compiler.stage)) + } + /// Returns the libdir where the standard library and other artifacts are /// found for a compiler's sysroot. fn sysroot_libdir(&self, compiler: &Compiler, target: &str) -> PathBuf { @@ -703,7 +723,8 @@ impl Build { fn llvm_filecheck(&self, target: &str) -> PathBuf { let target_config = self.config.target_config.get(target); if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { - s.parent().unwrap().join(exe("FileCheck", target)) + let llvm_bindir = output(Command::new(s).arg("--bindir")); + Path::new(llvm_bindir.trim()).join(exe("FileCheck", target)) } else { let base = self.llvm_out(&self.config.build).join("build"); let exe = exe("FileCheck", target); @@ -768,7 +789,7 @@ impl Build { /// Prints a message if this build is configured in verbose mode. fn verbose(&self, msg: &str) { - if self.flags.verbose || self.config.verbose { + if self.flags.verbose() || self.config.verbose() { println!("{}", msg); } } diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 1fa70081938..0d83a79cf32 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -56,7 +56,8 @@ check-cargotest: dist: $(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS) distcheck: - $(Q)$(BOOTSTRAP) test distcheck + $(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) test distcheck $(BOOTSTRAP_ARGS) install: $(Q)$(BOOTSTRAP) dist --install $(BOOTSTRAP_ARGS) tidy: @@ -65,7 +66,7 @@ tidy: check-stage2-T-arm-linux-androideabi-H-x86_64-unknown-linux-gnu: $(Q)$(BOOTSTRAP) test --target arm-linux-androideabi check-stage2-T-x86_64-unknown-linux-musl-H-x86_64-unknown-linux-gnu: - $(Q)$(BOOTSTRAP) test --target x86_64-unknown-linux-gnu + $(Q)$(BOOTSTRAP) test --target x86_64-unknown-linux-musl .PHONY: dist diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index ffa3fe1cbf2..09dbd9f8220 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -81,7 +81,7 @@ pub fn llvm(build: &Build, target: &str) { .profile(profile) .define("LLVM_ENABLE_ASSERTIONS", assertions) .define("LLVM_TARGETS_TO_BUILD", - "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430") + "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc") .define("LLVM_INCLUDE_EXAMPLES", "OFF") .define("LLVM_INCLUDE_TESTS", "OFF") .define("LLVM_INCLUDE_DOCS", "OFF") @@ -109,10 +109,10 @@ pub fn llvm(build: &Build, target: &str) { // MSVC handles compiler business itself if !target.contains("msvc") { - if build.config.ccache { - cfg.define("CMAKE_C_COMPILER", "ccache") + if let Some(ref ccache) = build.config.ccache { + cfg.define("CMAKE_C_COMPILER", ccache) .define("CMAKE_C_COMPILER_ARG1", build.cc(target)) - .define("CMAKE_CXX_COMPILER", "ccache") + .define("CMAKE_CXX_COMPILER", ccache) .define("CMAKE_CXX_COMPILER_ARG1", build.cxx(target)); } else { cfg.define("CMAKE_C_COMPILER", build.cc(target)) diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index f3fe22698bb..5d543419fc9 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -143,7 +143,7 @@ pub fn check(build: &mut Build) { // Externally configured LLVM requires FileCheck to exist let filecheck = build.llvm_filecheck(&build.config.build); if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests { - panic!("filecheck executable {:?} does not exist", filecheck); + panic!("FileCheck executable {:?} does not exist", filecheck); } for target in build.config.target.iter() { @@ -223,4 +223,8 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake if build.lldb_version.is_some() { build.lldb_python_dir = run(Command::new("lldb").arg("-P")).ok(); } + + if let Some(ref s) = build.config.ccache { + need_cmd(s.as_ref()); + } } diff --git a/src/bootstrap/step.rs b/src/bootstrap/step.rs index 884cc7da8ea..c5898c1119a 100644 --- a/src/bootstrap/step.rs +++ b/src/bootstrap/step.rs @@ -86,7 +86,7 @@ pub fn build_rules(build: &Build) -> Rules { // // To handle this we do a bit of dynamic dispatch to see what the dependency // is. If we're building a LLVM for the build triple, then we don't actually - // have any dependencies! To do that we return a dependency on the "dummy" + // have any dependencies! To do that we return a dependency on the `Step::noop()` // target which does nothing. // // If we're build a cross-compiled LLVM, however, we need to assemble the @@ -104,7 +104,7 @@ pub fn build_rules(build: &Build) -> Rules { .host(true) .dep(move |s| { if s.target == build.config.build { - dummy(s, build) + Step::noop() } else { s.target(&build.config.build) } @@ -115,14 +115,11 @@ pub fn build_rules(build: &Build) -> Rules { // going on here. You can check out the API docs below and also see a bunch // more examples of rules directly below as well. - // dummy rule to do nothing, useful when a dep maps to no deps - rules.build("dummy", "path/to/nowhere"); - // the compiler with no target libraries ready to go rules.build("rustc", "src/rustc") .dep(move |s| { if s.stage == 0 { - dummy(s, build) + Step::noop() } else { s.name("librustc") .host(&build.config.build) @@ -165,7 +162,7 @@ pub fn build_rules(build: &Build) -> Rules { .dep(move |s| s.name("rustc").host(&build.config.build).target(s.host)) .dep(move |s| { if s.host == build.config.build { - dummy(s, build) + Step::noop() } else { s.host(&build.config.build) } @@ -183,7 +180,7 @@ pub fn build_rules(build: &Build) -> Rules { .dep(|s| s.name("libstd")) .dep(move |s| { if s.host == build.config.build { - dummy(s, build) + Step::noop() } else { s.host(&build.config.build) } @@ -203,7 +200,7 @@ pub fn build_rules(build: &Build) -> Rules { .dep(move |s| s.name("llvm").host(&build.config.build).stage(0)) .dep(move |s| { if s.host == build.config.build { - dummy(s, build) + Step::noop() } else { s.host(&build.config.build) } @@ -233,7 +230,7 @@ pub fn build_rules(build: &Build) -> Rules { if s.target.contains("android") { s.name("android-copy-libs") } else { - dummy(s, build) + Step::noop() } }) .default(true) @@ -270,16 +267,18 @@ pub fn build_rules(build: &Build) -> Rules { // nothing to do for debuginfo tests } else if build.config.build.contains("apple") { rules.test("check-debuginfo", "src/test/debuginfo") + .default(true) .dep(|s| s.name("libtest")) - .dep(|s| s.name("tool-compiletest").host(s.host)) + .dep(|s| s.name("tool-compiletest").target(s.host)) .dep(|s| s.name("test-helpers")) .dep(|s| s.name("debugger-scripts")) .run(move |s| check::compiletest(build, &s.compiler(), s.target, "debuginfo-lldb", "debuginfo")); } else { rules.test("check-debuginfo", "src/test/debuginfo") + .default(true) .dep(|s| s.name("libtest")) - .dep(|s| s.name("tool-compiletest").host(s.host)) + .dep(|s| s.name("tool-compiletest").target(s.host)) .dep(|s| s.name("test-helpers")) .dep(|s| s.name("debugger-scripts")) .run(move |s| check::compiletest(build, &s.compiler(), s.target, @@ -458,7 +457,7 @@ pub fn build_rules(build: &Build) -> Rules { for (krate, path, default) in krates("test_shim") { rules.doc(&krate.doc_step, path) .dep(|s| s.name("libtest")) - .default(default && build.config.docs) + .default(default && build.config.compiler_docs) .run(move |s| doc::test(build, s.stage, s.target)); } for (krate, path, default) in krates("rustc-main") { @@ -490,16 +489,21 @@ pub fn build_rules(build: &Build) -> Rules { .default(true) .run(move |s| dist::std(build, &s.compiler(), s.target)); rules.dist("dist-mingw", "path/to/nowhere") - .run(move |s| dist::mingw(build, s.target)); + .default(true) + .run(move |s| { + if s.target.contains("pc-windows-gnu") { + dist::mingw(build, s.target) + } + }); rules.dist("dist-src", "src") .default(true) .host(true) - .run(move |_| dist::rust_src(build)); + .run(move |s| dist::rust_src(build, s.target)); rules.dist("dist-docs", "src/doc") .default(true) .dep(|s| s.name("default:doc")) .run(move |s| dist::docs(build, s.stage, s.target)); - rules.dist("dist-analysis", "src/libstd") + rules.dist("dist-analysis", "analysis") .dep(|s| s.name("dist-std")) .default(true) .run(move |s| dist::analysis(build, &s.compiler(), s.target)); @@ -509,12 +513,6 @@ pub fn build_rules(build: &Build) -> Rules { rules.verify(); return rules; - - fn dummy<'a>(s: &Step<'a>, build: &'a Build) -> Step<'a> { - s.name("dummy").stage(0) - .target(&build.config.build) - .host(&build.config.build) - } } #[derive(PartialEq, Eq, Hash, Clone, Debug)] @@ -538,6 +536,10 @@ struct Step<'a> { } impl<'a> Step<'a> { + fn noop() -> Step<'a> { + Step { name: "", stage: 0, host: "", target: "" } + } + /// Creates a new step which is the same as this, except has a new name. fn name(&self, name: &'a str) -> Step<'a> { Step { name: name, ..*self } @@ -733,6 +735,9 @@ impl<'a> Rules<'a> { if self.rules.contains_key(&dep.name) || dep.name.starts_with("default:") { continue } + if dep == Step::noop() { + continue + } panic!("\ invalid rule dependency graph detected, was a rule added and maybe typo'd? @@ -817,7 +822,16 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? let hosts = if self.build.flags.host.len() > 0 { &self.build.flags.host } else { - &self.build.config.host + if kind == Kind::Dist { + // For 'dist' steps we only distribute artifacts built from + // the build platform, so only consider that in the hosts + // array. + // NOTE: This relies on the fact that the build triple is + // always placed first, as done in `config.rs`. + &self.build.config.host[..1] + } else { + &self.build.config.host + } }; let targets = if self.build.flags.target.len() > 0 { &self.build.flags.target @@ -859,6 +873,7 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? // of what we need to do. let mut order = Vec::new(); let mut added = HashSet::new(); + added.insert(Step::noop()); for step in steps.iter().cloned() { self.fill(step, &mut order, &mut added); } @@ -871,6 +886,10 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? // And finally, iterate over everything and execute it. for step in order.iter() { + if self.build.flags.keep_stage.map_or(false, |s| step.stage <= s) { + self.build.verbose(&format!("keeping step {:?}", step)); + continue; + } self.build.verbose(&format!("executing step {:?}", step)); (self.rules[step.name].run)(step); } diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index cb5b456a0f2..c9e756b6f99 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -41,6 +41,12 @@ pub fn mtime(path: &Path) -> FileTime { /// Copies a file from `src` to `dst`, attempting to use hard links and then /// falling back to an actually filesystem copy if necessary. pub fn copy(src: &Path, dst: &Path) { + // A call to `hard_link` will fail if `dst` exists, so remove it if it + // already exists so we can try to help `hard_link` succeed. + let _ = fs::remove_file(&dst); + + // Attempt to "easy copy" by creating a hard link (symlinks don't work on + // windows), but if that fails just fall back to a slow `copy` operation. let res = fs::hard_link(src, dst); let res = res.or_else(|_| fs::copy(src, dst).map(|_| ())); if let Err(e) = res { diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs index 07f9c91d3c7..d0d588f46a7 100644 --- a/src/build_helper/lib.rs +++ b/src/build_helper/lib.rs @@ -47,6 +47,8 @@ pub fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> { None } else if target.contains("musl") { Some(PathBuf::from("ar")) + } else if target.contains("openbsd") { + Some(PathBuf::from("ar")) } else { let parent = cc.parent().unwrap(); let file = cc.file_name().unwrap().to_str().unwrap(); @@ -61,6 +63,16 @@ pub fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> { } } +pub fn make(host: &str) -> PathBuf { + if host.contains("bitrig") || host.contains("dragonfly") || + host.contains("freebsd") || host.contains("netbsd") || + host.contains("openbsd") { + PathBuf::from("gmake") + } else { + PathBuf::from("make") + } +} + pub fn output(cmd: &mut Command) -> String { let output = match cmd.stderr(Stdio::inherit()).output() { Ok(status) => status, diff --git a/src/ci/docker/arm-android/Dockerfile b/src/ci/docker/arm-android/Dockerfile index 121c0263cbc..8911b4ff0cb 100644 --- a/src/ci/docker/arm-android/Dockerfile +++ b/src/ci/docker/arm-android/Dockerfile @@ -16,7 +16,8 @@ RUN dpkg --add-architecture i386 && \ expect \ openjdk-9-jre \ sudo \ - libstdc++6:i386 + libstdc++6:i386 \ + xz-utils WORKDIR /android/ ENV PATH=$PATH:/android/ndk-arm-9/bin:/android/sdk/tools:/android/sdk/platform-tools @@ -25,8 +26,17 @@ COPY install-ndk.sh install-sdk.sh accept-licenses.sh /android/ RUN sh /android/install-ndk.sh RUN sh /android/install-sdk.sh +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb + COPY start-emulator.sh /android/ -ENTRYPOINT ["/android/start-emulator.sh"] + +ENTRYPOINT ["/usr/bin/dumb-init", "--", "/android/start-emulator.sh"] + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 ENV TARGETS=arm-linux-androideabi ENV TARGETS=$TARGETS,i686-linux-android diff --git a/src/ci/docker/cross/Dockerfile b/src/ci/docker/cross/Dockerfile index b7b23d74c9d..08b436313f6 100644 --- a/src/ci/docker/cross/Dockerfile +++ b/src/ci/docker/cross/Dockerfile @@ -21,7 +21,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ gcc-powerpc-linux-gnu libc6-dev-powerpc-cross \ gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross \ gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross \ - gcc-s390x-linux-gnu libc6-dev-s390x-cross + gcc-s390x-linux-gnu libc6-dev-s390x-cross \ + xz-utils + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV TARGETS=aarch64-unknown-linux-gnu ENV TARGETS=$TARGETS,arm-unknown-linux-gnueabi diff --git a/src/ci/docker/i686-gnu-nopt/Dockerfile b/src/ci/docker/i686-gnu-nopt/Dockerfile index a9ef29daaf1..1da33c94c7b 100644 --- a/src/ci/docker/i686-gnu-nopt/Dockerfile +++ b/src/ci/docker/i686-gnu-nopt/Dockerfile @@ -11,7 +11,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ ccache \ sudo \ - gdb + gdb \ + xz-utils + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests ENV RUST_CHECK_TARGET check diff --git a/src/ci/docker/i686-gnu/Dockerfile b/src/ci/docker/i686-gnu/Dockerfile index d0ddde95b44..9e5b0e0435e 100644 --- a/src/ci/docker/i686-gnu/Dockerfile +++ b/src/ci/docker/i686-gnu/Dockerfile @@ -11,7 +11,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ ccache \ sudo \ - gdb + gdb \ + xz-utils + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu ENV RUST_CHECK_TARGET check diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index ff5345d3aac..8c2c8d2a9db 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -25,18 +25,26 @@ docker \ -t rust-ci \ "`dirname "$script"`/$image" -mkdir -p $HOME/.ccache mkdir -p $HOME/.cargo mkdir -p $root_dir/obj +args= +if [ "$SCCACHE_BUCKET" != "" ]; then + args="$args --env SCCACHE_BUCKET=$SCCACHE_BUCKET" + args="$args --env AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" + args="$args --env AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" +else + mkdir -p $HOME/.cache/sccache + args="$args --env SCCACHE_DIR=/sccache --volume $HOME/.cache/sccache:/sccache" +fi + exec docker \ run \ --volume "$root_dir:/checkout:ro" \ --volume "$root_dir/obj:/checkout/obj" \ --workdir /checkout/obj \ --env SRC=/checkout \ - --env CCACHE_DIR=/ccache \ - --volume "$HOME/.ccache:/ccache" \ + $args \ --env CARGO_HOME=/cargo \ --env LOCAL_USER_ID=`id -u` \ --volume "$HOME/.cargo:/cargo" \ diff --git a/src/ci/docker/x86_64-freebsd/Dockerfile b/src/ci/docker/x86_64-freebsd/Dockerfile index a3a52f9e6ff..75f3174e2c0 100644 --- a/src/ci/docker/x86_64-freebsd/Dockerfile +++ b/src/ci/docker/x86_64-freebsd/Dockerfile @@ -18,6 +18,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY build-toolchain.sh /tmp/ RUN sh /tmp/build-toolchain.sh +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + ENV \ AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-ar \ CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-gcc diff --git a/src/ci/docker/x86_64-gnu-cargotest/Dockerfile b/src/ci/docker/x86_64-gnu-cargotest/Dockerfile index 107e2bf8a12..2c3db87d9fb 100644 --- a/src/ci/docker/x86_64-gnu-cargotest/Dockerfile +++ b/src/ci/docker/x86_64-gnu-cargotest/Dockerfile @@ -11,7 +11,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ ccache \ libssl-dev \ - sudo + sudo \ + xz-utils \ + pkg-config + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu ENV RUST_CHECK_TARGET check-cargotest diff --git a/src/ci/docker/x86_64-gnu-debug/Dockerfile b/src/ci/docker/x86_64-gnu-debug/Dockerfile index 9e98215775e..eec88442293 100644 --- a/src/ci/docker/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/x86_64-gnu-debug/Dockerfile @@ -11,7 +11,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ ccache \ sudo \ - gdb + gdb \ + xz-utils + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ diff --git a/src/ci/docker/x86_64-gnu-llvm-3.7/Dockerfile b/src/ci/docker/x86_64-gnu-llvm-3.7/Dockerfile index c27e3d1325f..4c9198d88eb 100644 --- a/src/ci/docker/x86_64-gnu-llvm-3.7/Dockerfile +++ b/src/ci/docker/x86_64-gnu-llvm-3.7/Dockerfile @@ -14,7 +14,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ gdb \ llvm-3.7-tools \ libedit-dev \ - zlib1g-dev + zlib1g-dev \ + xz-utils + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ diff --git a/src/ci/docker/x86_64-gnu-make/Dockerfile b/src/ci/docker/x86_64-gnu-make/Dockerfile index 93229b2a010..1c503aea13d 100644 --- a/src/ci/docker/x86_64-gnu-make/Dockerfile +++ b/src/ci/docker/x86_64-gnu-make/Dockerfile @@ -11,7 +11,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ ccache \ sudo \ - gdb + gdb \ + xz-utils + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --disable-rustbuild ENV RUST_CHECK_TARGET check diff --git a/src/ci/docker/x86_64-gnu-nopt/Dockerfile b/src/ci/docker/x86_64-gnu-nopt/Dockerfile index 73a3e2c726c..66de6ea13ac 100644 --- a/src/ci/docker/x86_64-gnu-nopt/Dockerfile +++ b/src/ci/docker/x86_64-gnu-nopt/Dockerfile @@ -11,7 +11,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ ccache \ sudo \ - gdb + gdb \ + xz-utils + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --disable-optimize-tests ENV RUST_CHECK_TARGET check diff --git a/src/ci/docker/x86_64-gnu/Dockerfile b/src/ci/docker/x86_64-gnu/Dockerfile index f125693e7ae..3d71b7ffb9a 100644 --- a/src/ci/docker/x86_64-gnu/Dockerfile +++ b/src/ci/docker/x86_64-gnu/Dockerfile @@ -11,7 +11,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ ccache \ sudo \ - gdb + gdb \ + xz-utils + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu ENV RUST_CHECK_TARGET check diff --git a/src/ci/docker/x86_64-musl/Dockerfile b/src/ci/docker/x86_64-musl/Dockerfile index 967940fb1f3..96b38067cbb 100644 --- a/src/ci/docker/x86_64-musl/Dockerfile +++ b/src/ci/docker/x86_64-musl/Dockerfile @@ -18,6 +18,15 @@ WORKDIR /build/ COPY build-musl.sh /build/ RUN sh /build/build-musl.sh && rm -rf /build +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + ENV RUST_CONFIGURE_ARGS \ --target=x86_64-unknown-linux-musl \ --musl-root-x86_64=/musl-x86_64 diff --git a/src/ci/run.sh b/src/ci/run.sh index 10f2d15da34..152694346aa 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -26,17 +26,13 @@ if [ "$NO_VENDOR" = "" ]; then ENABLE_VENDOR=--enable-vendor fi -if [ "$NO_CCACHE" = "" ]; then - ENABLE_CCACHE=--enable-ccache -fi - set -ex $SRC/configure \ --disable-manage-submodules \ --enable-debug-assertions \ --enable-quiet-tests \ - $ENABLE_CCACHE \ + --enable-sccache \ $ENABLE_VENDOR \ $ENABLE_LLVM_ASSERTIONS \ $RUST_CONFIGURE_ARGS diff --git a/src/doc/book/casting-between-types.md b/src/doc/book/casting-between-types.md index 296384ab6ef..a8f8224c58b 100644 --- a/src/doc/book/casting-between-types.md +++ b/src/doc/book/casting-between-types.md @@ -16,18 +16,18 @@ function result. The most common case of coercion is removing mutability from a reference: - * `&mut T` to `&T` +* `&mut T` to `&T` An analogous conversion is to remove mutability from a [raw pointer](raw-pointers.md): - * `*mut T` to `*const T` +* `*mut T` to `*const T` References can also be coerced to raw pointers: - * `&T` to `*const T` +* `&T` to `*const T` - * `&mut T` to `*mut T` +* `&mut T` to `*mut T` Custom coercions may be defined using [`Deref`](deref-coercions.md). @@ -59,11 +59,11 @@ A cast `e as U` is valid if `e` has type `T` and `T` *coerces* to `U`. A cast `e as U` is also valid in any of the following cases: - * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* - * `e` is a C-like enum (with no data attached to the variants), - and `U` is an integer type; *enum-cast* - * `e` has type `bool` or `char` and `U` is an integer type; *prim-int-cast* - * `e` has type `u8` and `U` is `char`; *u8-char-cast* +* `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* +* `e` is a C-like enum (with no data attached to the variants), + and `U` is an integer type; *enum-cast* +* `e` has type `bool` or `char` and `U` is an integer type; *prim-int-cast* +* `e` has type `u8` and `U` is `char`; *u8-char-cast* For example diff --git a/src/doc/book/documentation.md b/src/doc/book/documentation.md index f30a95b4e78..dafcffc39c8 100644 --- a/src/doc/book/documentation.md +++ b/src/doc/book/documentation.md @@ -460,8 +460,9 @@ not actually pass as a test. ``` The `no_run` attribute will compile your code, but not run it. This is -important for examples such as "Here's how to start up a network service," -which you would want to make sure compile, but might run in an infinite loop! +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. ### Documenting modules diff --git a/src/doc/book/getting-started.md b/src/doc/book/getting-started.md index 2eab449dbd4..5aae693ad6b 100644 --- a/src/doc/book/getting-started.md +++ b/src/doc/book/getting-started.md @@ -24,41 +24,40 @@ see the website][platform-support]. [platform-support]: https://forge.rust-lang.org/platform-support.html -## Installing on Linux or Mac +## Installing Rust -If we're on Linux or a Mac, all we need to do is open a terminal and type this: +All you need to do on Unix systems like Linux and macOS is open a +terminal and type this: ```bash -$ curl -sSf https://static.rust-lang.org/rustup.sh | sh +$ curl https://sh.rustup.rs -sSf | sh ``` -This will download a script, and start the installation. If it all goes well, -you’ll see this appear: +It will download a script, and start the installation. If everything +goes well, you’ll see this appear: ```text -Rust is ready to roll. +Rust is installed now. Great! ``` -From here, press `y` for ‘yes’, and then follow the rest of the prompts. +Installing on Windows is nearly as easy: download and run +[rustup-init.exe]. It will start the installation in a console and +present the above message on success. -## Installing on Windows +For other installation options and information, visit the [install] +page of the Rust website. -If you're on Windows, please download the appropriate [installer][install-page]. - -[install-page]: https://www.rust-lang.org/install.html +[rustup-init.exe]: https://win.rustup.rs +[install]: https://www.rust-lang.org/install.html ## Uninstalling -Uninstalling Rust is as easy as installing it. On Linux or Mac, run -the uninstall script: +Uninstalling Rust is as easy as installing it: ```bash -$ sudo /usr/local/lib/rustlib/uninstall.sh +$ rustup self uninstall ``` -If we used the Windows installer, we can re-run the `.msi` and it will give us -an uninstall option. - ## Troubleshooting If we've got Rust installed, we can open up a shell, and type this: @@ -71,12 +70,15 @@ You should see the version number, commit hash, and commit date. If you do, Rust has been installed successfully! Congrats! -If you don't and you're on Windows, check that Rust is in your %PATH% system -variable: `$ echo %PATH%`. If it isn't, run the installer again, select "Change" -on the "Change, repair, or remove installation" page and ensure "Add to PATH" is -installed on the local hard drive. If you need to configure your path manually, -you can find the Rust executables in a directory like -`"C:\Program Files\Rust stable GNU 1.x\bin"`. +If you don't, that probably means that the `PATH` environment variable +doesn't include Cargo's binary directory, `~/.cargo/bin` on Unix, or +`%USERPROFILE%\.cargo\bin` on Windows. This is the directory where +Rust development tools live, and most Rust developers keep it in their +`PATH` environment variable, which makes it possible to run `rustc` on +the command line. Due to differences in operating systems, command +shells, and bugs in installation, you may need to restart your shell, +log out of the system, or configure `PATH` manually as appropriate for +your operating environment. Rust does not do its own linking, and so you’ll need to have a linker installed. Doing so will depend on your specific system. For @@ -106,9 +108,7 @@ resources include [the user’s forum][users] and [Stack Overflow][stackoverflow [stackoverflow]: http://stackoverflow.com/questions/tagged/rust This installer also installs a copy of the documentation locally, so we can -read it offline. On UNIX systems, `/usr/local/share/doc/rust` is the location. -On Windows, it's in a `share/doc` directory, inside the directory to which Rust -was installed. +read it offline. It's only a `rustup doc` away! # Hello, world! diff --git a/src/doc/book/testing.md b/src/doc/book/testing.md index ebeb9923197..96cec7295aa 100644 --- a/src/doc/book/testing.md +++ b/src/doc/book/testing.md @@ -589,11 +589,10 @@ please see the [Documentation chapter](documentation.html). # Testing and concurrency -One thing that is important to note when writing tests is that they may be run -concurrently using threads. For this reason you should take care that your tests -are written in such a way as to not depend on each-other, or on any shared -state. "Shared state" can also include the environment, such as the current -working directory, or environment variables. +It is important to note that tests are run concurrently using threads. For this +reason, care should be taken to ensure your tests do not depend on each-other, +or on any shared state. "Shared state" can also include the environment, such +as the current working directory, or environment variables. If this is an issue it is possible to control this concurrency, either by setting the environment variable `RUST_TEST_THREADS`, or by passing the argument diff --git a/src/doc/reference.md b/src/doc/reference.md index bf286aaec4b..b5a91a170d8 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -1731,7 +1731,8 @@ of an item to see whether it should be allowed or not. This is where privacy warnings are generated, or otherwise "you used a private item of another module and weren't allowed to." -By default, everything in Rust is *private*, with one exception. Enum variants +By default, everything in Rust is *private*, with two exceptions: Associated +items in a `pub` Trait are public by default; Enum variants in a `pub` enum are also public by default. When an item is declared as `pub`, it can be thought of as being accessible to the outside world. For example: diff --git a/src/doc/rust.css b/src/doc/rust.css index 932594b9912..664bc0fdab0 100644 --- a/src/doc/rust.css +++ b/src/doc/rust.css @@ -44,7 +44,9 @@ font-family: 'Source Code Pro'; font-style: normal; font-weight: 400; - src: local('Source Code Pro'), url("SourceCodePro-Regular.woff") format('woff'); + /* Avoid using locally installed font because bad versions are in circulation: + * see https://github.com/rust-lang/rust/issues/24355 */ + src: url("SourceCodePro-Regular.woff") format('woff'); } *:not(body) { diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 3a7da18c8de..1cad8f7f407 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -392,8 +392,6 @@ impl<T: ?Sized> Arc<T> { /// # Examples /// /// ``` - /// #![feature(arc_counts)] - /// /// use std::sync::Arc; /// /// let five = Arc::new(5); @@ -404,8 +402,7 @@ impl<T: ?Sized> Arc<T> { /// assert_eq!(1, Arc::weak_count(&five)); /// ``` #[inline] - #[unstable(feature = "arc_counts", reason = "not clearly useful, and racy", - issue = "28356")] + #[stable(feature = "arc_counts", since = "1.15.0")] pub fn weak_count(this: &Self) -> usize { this.inner().weak.load(SeqCst) - 1 } @@ -421,8 +418,6 @@ impl<T: ?Sized> Arc<T> { /// # Examples /// /// ``` - /// #![feature(arc_counts)] - /// /// use std::sync::Arc; /// /// let five = Arc::new(5); @@ -433,8 +428,7 @@ impl<T: ?Sized> Arc<T> { /// assert_eq!(2, Arc::strong_count(&five)); /// ``` #[inline] - #[unstable(feature = "arc_counts", reason = "not clearly useful, and racy", - issue = "28356")] + #[stable(feature = "arc_counts", since = "1.15.0")] pub fn strong_count(this: &Self) -> usize { this.inner().strong.load(SeqCst) } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 12809171b74..81ed4be7763 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -127,6 +127,7 @@ pub fn usable_size(size: usize, align: usize) -> usize { pub const EMPTY: *mut () = 0x1 as *mut (); /// The allocator for unique pointers. +// This function must not unwind. If it does, MIR trans will fail. #[cfg(not(test))] #[lang = "exchange_malloc"] #[inline] @@ -143,6 +144,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { } #[cfg(not(test))] +#[cfg(stage0)] #[lang = "exchange_free"] #[inline] unsafe fn exchange_free(ptr: *mut u8, old_size: usize, align: usize) { diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index d1e0e333b8f..86f8c746646 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -320,7 +320,7 @@ impl<T> Rc<T> { #[inline] #[stable(feature = "rc_unique", since = "1.4.0")] pub fn try_unwrap(this: Self) -> Result<T, Self> { - if Rc::would_unwrap(&this) { + if Rc::strong_count(&this) == 1 { unsafe { let val = ptr::read(&*this); // copy the contained object @@ -343,26 +343,10 @@ impl<T> Rc<T> { /// /// [try_unwrap]: struct.Rc.html#method.try_unwrap /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok - /// - /// # Examples - /// - /// ``` - /// #![feature(rc_would_unwrap)] - /// - /// use std::rc::Rc; - /// - /// let x = Rc::new(3); - /// assert!(Rc::would_unwrap(&x)); - /// assert_eq!(Rc::try_unwrap(x), Ok(3)); - /// - /// let x = Rc::new(4); - /// let _y = x.clone(); - /// assert!(!Rc::would_unwrap(&x)); - /// assert_eq!(*Rc::try_unwrap(x).unwrap_err(), 4); - /// ``` #[unstable(feature = "rc_would_unwrap", reason = "just added for niche usecase", issue = "28356")] + #[rustc_deprecated(since = "1.15.0", reason = "too niche; use `strong_count` instead")] pub fn would_unwrap(this: &Self) -> bool { Rc::strong_count(&this) == 1 } @@ -482,8 +466,6 @@ impl<T: ?Sized> Rc<T> { /// # Examples /// /// ``` - /// #![feature(rc_counts)] - /// /// use std::rc::Rc; /// /// let five = Rc::new(5); @@ -492,8 +474,7 @@ impl<T: ?Sized> Rc<T> { /// assert_eq!(1, Rc::weak_count(&five)); /// ``` #[inline] - #[unstable(feature = "rc_counts", reason = "not clearly useful", - issue = "28356")] + #[stable(feature = "rc_counts", since = "1.15.0")] pub fn weak_count(this: &Self) -> usize { this.weak() - 1 } @@ -503,8 +484,6 @@ impl<T: ?Sized> Rc<T> { /// # Examples /// /// ``` - /// #![feature(rc_counts)] - /// /// use std::rc::Rc; /// /// let five = Rc::new(5); @@ -513,8 +492,7 @@ impl<T: ?Sized> Rc<T> { /// assert_eq!(2, Rc::strong_count(&five)); /// ``` #[inline] - #[unstable(feature = "rc_counts", reason = "not clearly useful", - issue = "28356")] + #[stable(feature = "rc_counts", since = "1.15.0")] pub fn strong_count(this: &Self) -> usize { this.strong() } @@ -523,21 +501,11 @@ impl<T: ?Sized> Rc<T> { /// this inner value. /// /// [weak]: struct.Weak.html - /// - /// # Examples - /// - /// ``` - /// #![feature(rc_counts)] - /// - /// use std::rc::Rc; - /// - /// let five = Rc::new(5); - /// - /// assert!(Rc::is_unique(&five)); - /// ``` #[inline] - #[unstable(feature = "rc_counts", reason = "uniqueness has unclear meaning", + #[unstable(feature = "is_unique", reason = "uniqueness has unclear meaning", issue = "28356")] + #[rustc_deprecated(since = "1.15.0", + reason = "too niche; use `strong_count` and `weak_count` instead")] pub fn is_unique(this: &Self) -> bool { Rc::weak_count(this) == 0 && Rc::strong_count(this) == 1 } diff --git a/src/liballoc_jemalloc/build.rs b/src/liballoc_jemalloc/build.rs index fc849e7a50c..2bbec9e5fc2 100644 --- a/src/liballoc_jemalloc/build.rs +++ b/src/liballoc_jemalloc/build.rs @@ -36,7 +36,8 @@ fn main() { // targets, which means we have to build the alloc_jemalloc crate // for targets like emscripten, even if we don't use it. if target.contains("rumprun") || target.contains("bitrig") || target.contains("openbsd") || - target.contains("msvc") || target.contains("emscripten") || target.contains("fuchsia") { + target.contains("msvc") || target.contains("emscripten") || target.contains("fuchsia") || + target.contains("redox") { println!("cargo:rustc-cfg=dummy_jemalloc"); return; } @@ -151,7 +152,7 @@ fn main() { cmd.arg(format!("--build={}", build_helper::gnu_target(&host))); run(&mut cmd); - let mut make = Command::new("make"); + let mut make = Command::new(build_helper::make(&host)); make.current_dir(&build_dir) .arg("build_lib_static"); diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index a4fabb5a2c9..4daa6cbb846 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -19,7 +19,7 @@ issue = "27783")] #![feature(allocator)] #![feature(staged_api)] -#![cfg_attr(unix, feature(libc))] +#![cfg_attr(any(unix, target_os = "redox"), feature(libc))] // The minimum alignment guaranteed by the architecture. This value is used to // add fast paths for low alignment values. In practice, the alignment is a @@ -71,7 +71,7 @@ pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize { imp::usable_size(size, align) } -#[cfg(unix)] +#[cfg(any(unix, target_os = "redox"))] mod imp { extern crate libc; @@ -87,7 +87,7 @@ mod imp { } } - #[cfg(target_os = "android")] + #[cfg(any(target_os = "android", target_os = "redox"))] unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 { // On android we currently target API level 9 which unfortunately // doesn't have the `posix_memalign` API used below. Instead we use @@ -109,7 +109,7 @@ mod imp { libc::memalign(align as libc::size_t, size as libc::size_t) as *mut u8 } - #[cfg(not(target_os = "android"))] + #[cfg(not(any(target_os = "android", target_os = "redox")))] unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 { let mut out = ptr::null_mut(); let ret = libc::posix_memalign(&mut out, align as libc::size_t, size as libc::size_t); diff --git a/src/libcollections/binary_heap.rs b/src/libcollections/binary_heap.rs index 8d0c76c3646..c5d5ad27d23 100644 --- a/src/libcollections/binary_heap.rs +++ b/src/libcollections/binary_heap.rs @@ -225,7 +225,7 @@ pub struct BinaryHeap<T> { /// [`peek_mut()`]: struct.BinaryHeap.html#method.peek_mut #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] pub struct PeekMut<'a, T: 'a + Ord> { - heap: &'a mut BinaryHeap<T> + heap: &'a mut BinaryHeap<T>, } #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] @@ -385,9 +385,7 @@ impl<T: Ord> BinaryHeap<T> { if self.is_empty() { None } else { - Some(PeekMut { - heap: self - }) + Some(PeekMut { heap: self }) } } @@ -1126,7 +1124,9 @@ impl<T: Ord> IntoIterator for BinaryHeap<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a BinaryHeap<T> where T: Ord { +impl<'a, T> IntoIterator for &'a BinaryHeap<T> + where T: Ord +{ type Item = &'a T; type IntoIter = Iter<'a, T>; diff --git a/src/libcollections/borrow.rs b/src/libcollections/borrow.rs index 37618b7600a..e5bcf0d8e81 100644 --- a/src/libcollections/borrow.rs +++ b/src/libcollections/borrow.rs @@ -63,7 +63,9 @@ pub trait ToOwned { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T> ToOwned for T where T: Clone { +impl<T> ToOwned for T + where T: Clone +{ type Owned = T; fn to_owned(&self) -> T { self.clone() @@ -117,17 +119,19 @@ pub enum Cow<'a, B: ?Sized + 'a> { /// Borrowed data. #[stable(feature = "rust1", since = "1.0.0")] - Borrowed(#[stable(feature = "rust1", since = "1.0.0")] &'a B), + Borrowed(#[stable(feature = "rust1", since = "1.0.0")] + &'a B), /// Owned data. #[stable(feature = "rust1", since = "1.0.0")] - Owned( - #[stable(feature = "rust1", since = "1.0.0")] <B as ToOwned>::Owned - ), + Owned(#[stable(feature = "rust1", since = "1.0.0")] + <B as ToOwned>::Owned), } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, B: ?Sized> Clone for Cow<'a, B> where B: ToOwned { +impl<'a, B: ?Sized> Clone for Cow<'a, B> + where B: ToOwned +{ fn clone(&self) -> Cow<'a, B> { match *self { Borrowed(b) => Borrowed(b), @@ -139,7 +143,9 @@ impl<'a, B: ?Sized> Clone for Cow<'a, B> where B: ToOwned { } } -impl<'a, B: ?Sized> Cow<'a, B> where B: ToOwned { +impl<'a, B: ?Sized> Cow<'a, B> + where B: ToOwned +{ /// Acquires a mutable reference to the owned form of the data. /// /// Clones the data if it is not already owned. @@ -194,7 +200,9 @@ impl<'a, B: ?Sized> Cow<'a, B> where B: ToOwned { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, B: ?Sized> Deref for Cow<'a, B> where B: ToOwned { +impl<'a, B: ?Sized> Deref for Cow<'a, B> + where B: ToOwned +{ type Target = B; fn deref(&self) -> &B { @@ -209,7 +217,9 @@ impl<'a, B: ?Sized> Deref for Cow<'a, B> where B: ToOwned { impl<'a, B: ?Sized> Eq for Cow<'a, B> where B: Eq + ToOwned {} #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, B: ?Sized> Ord for Cow<'a, B> where B: Ord + ToOwned { +impl<'a, B: ?Sized> Ord for Cow<'a, B> + where B: Ord + ToOwned +{ #[inline] fn cmp(&self, other: &Cow<'a, B>) -> Ordering { Ord::cmp(&**self, &**other) @@ -228,7 +238,9 @@ impl<'a, 'b, B: ?Sized, C: ?Sized> PartialEq<Cow<'b, C>> for Cow<'a, B> } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, B: ?Sized> PartialOrd for Cow<'a, B> where B: PartialOrd + ToOwned { +impl<'a, B: ?Sized> PartialOrd for Cow<'a, B> + where B: PartialOrd + ToOwned +{ #[inline] fn partial_cmp(&self, other: &Cow<'a, B>) -> Option<Ordering> { PartialOrd::partial_cmp(&**self, &**other) @@ -273,7 +285,9 @@ impl<'a, B: ?Sized> Default for Cow<'a, B> } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, B: ?Sized> Hash for Cow<'a, B> where B: Hash + ToOwned { +impl<'a, B: ?Sized> Hash for Cow<'a, B> + where B: Hash + ToOwned +{ #[inline] fn hash<H: Hasher>(&self, state: &mut H) { Hash::hash(&**self, state) diff --git a/src/libcollections/btree/set.rs b/src/libcollections/btree/set.rs index c57266d9e3b..34674e3a0bd 100644 --- a/src/libcollections/btree/set.rs +++ b/src/libcollections/btree/set.rs @@ -74,24 +74,44 @@ pub struct BTreeSet<T> { map: BTreeMap<T, ()>, } -/// An iterator over a BTreeSet's items. +/// An iterator over a `BTreeSet`'s items. +/// +/// This structure is created by the [`iter`] method on [`BTreeSet`]. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`iter`]: struct.BTreeSet.html#method.iter #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { iter: Keys<'a, T, ()>, } -/// An owning iterator over a BTreeSet's items. +/// An owning iterator over a `BTreeSet`'s items. +/// +/// This structure is created by the `into_iter` method on [`BTreeSet`] +/// [`BTreeSet`] (provided by the `IntoIterator` trait). +/// +/// [`BTreeSet`]: struct.BTreeSet.html #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter<T> { iter: ::btree_map::IntoIter<T, ()>, } -/// An iterator over a sub-range of BTreeSet's items. +/// An iterator over a sub-range of `BTreeSet`'s items. +/// +/// This structure is created by the [`range`] method on [`BTreeSet`]. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`range`]: struct.BTreeSet.html#method.range pub struct Range<'a, T: 'a> { iter: ::btree_map::Range<'a, T, ()>, } /// A lazy iterator producing elements in the set difference (in-order). +/// +/// This structure is created by the [`difference`] method on [`BTreeSet`]. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`difference`]: struct.BTreeSet.html#method.difference #[stable(feature = "rust1", since = "1.0.0")] pub struct Difference<'a, T: 'a> { a: Peekable<Iter<'a, T>>, @@ -99,6 +119,12 @@ pub struct Difference<'a, T: 'a> { } /// A lazy iterator producing elements in the set symmetric difference (in-order). +/// +/// This structure is created by the [`symmetric_difference`] method on +/// [`BTreeSet`]. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`symmetric_difference`]: struct.BTreeSet.html#method.symmetric_difference #[stable(feature = "rust1", since = "1.0.0")] pub struct SymmetricDifference<'a, T: 'a> { a: Peekable<Iter<'a, T>>, @@ -106,6 +132,11 @@ pub struct SymmetricDifference<'a, T: 'a> { } /// A lazy iterator producing elements in the set intersection (in-order). +/// +/// This structure is created by the [`intersection`] method on [`BTreeSet`]. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`intersection`]: struct.BTreeSet.html#method.intersection #[stable(feature = "rust1", since = "1.0.0")] pub struct Intersection<'a, T: 'a> { a: Peekable<Iter<'a, T>>, @@ -113,6 +144,11 @@ pub struct Intersection<'a, T: 'a> { } /// A lazy iterator producing elements in the set union (in-order). +/// +/// This structure is created by the [`union`] method on [`BTreeSet`]. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`union`]: struct.BTreeSet.html#method.union #[stable(feature = "rust1", since = "1.0.0")] pub struct Union<'a, T: 'a> { a: Peekable<Iter<'a, T>>, @@ -120,7 +156,7 @@ pub struct Union<'a, T: 'a> { } impl<T: Ord> BTreeSet<T> { - /// Makes a new BTreeSet with a reasonable choice of B. + /// Makes a new `BTreeSet` with a reasonable choice of B. /// /// # Examples /// @@ -137,21 +173,32 @@ impl<T: Ord> BTreeSet<T> { } impl<T> BTreeSet<T> { - /// Gets an iterator over the BTreeSet's contents. + /// Gets an iterator that visits the values in the `BTreeSet` in ascending order. /// /// # Examples /// /// ``` /// use std::collections::BTreeSet; /// - /// let set: BTreeSet<usize> = [1, 2, 3, 4].iter().cloned().collect(); + /// let set: BTreeSet<usize> = [1, 2, 3].iter().cloned().collect(); + /// let mut set_iter = set.iter(); + /// assert_eq!(set_iter.next(), Some(&1)); + /// assert_eq!(set_iter.next(), Some(&2)); + /// assert_eq!(set_iter.next(), Some(&3)); + /// assert_eq!(set_iter.next(), None); + /// ``` /// - /// for x in set.iter() { - /// println!("{}", x); - /// } + /// Values returned by the iterator are returned in ascending order: /// - /// let v: Vec<_> = set.iter().cloned().collect(); - /// assert_eq!(v, [1, 2, 3, 4]); + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet<usize> = [3, 1, 2].iter().cloned().collect(); + /// let mut set_iter = set.iter(); + /// assert_eq!(set_iter.next(), Some(&1)); + /// assert_eq!(set_iter.next(), Some(&2)); + /// assert_eq!(set_iter.next(), Some(&3)); + /// assert_eq!(set_iter.next(), None); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn iter(&self) -> Iter<T> { diff --git a/src/libcollections/enum_set.rs b/src/libcollections/enum_set.rs index 79e0021b148..87bc5e59ef7 100644 --- a/src/libcollections/enum_set.rs +++ b/src/libcollections/enum_set.rs @@ -276,7 +276,8 @@ impl<E: CLike> FromIterator<E> for EnumSet<E> { } } -impl<'a, E> IntoIterator for &'a EnumSet<E> where E: CLike +impl<'a, E> IntoIterator for &'a EnumSet<E> + where E: CLike { type Item = E; type IntoIter = Iter<E>; diff --git a/src/libcollections/linked_list.rs b/src/libcollections/linked_list.rs index 67f3708a62b..31085509088 100644 --- a/src/libcollections/linked_list.rs +++ b/src/libcollections/linked_list.rs @@ -10,8 +10,15 @@ //! A doubly-linked list with owned nodes. //! -//! The `LinkedList` allows pushing and popping elements at either end and is thus -//! efficiently usable as a double-ended queue. +//! The `LinkedList` allows pushing and popping elements at either end +//! in constant time. +//! +//! Almost always it is better to use `Vec` or [`VecDeque`] instead of +//! [`LinkedList`]. In general, array-based containers are faster, +//! more memory efficient and make better use of CPU cache. +//! +//! [`LinkedList`]: ../linked_list/struct.LinkedList.html +//! [`VecDeque`]: ../vec_deque/struct.VecDeque.html #![stable(feature = "rust1", since = "1.0.0")] @@ -27,7 +34,14 @@ use core::ptr::{self, Shared}; use super::SpecExtend; -/// A doubly-linked list. +/// A doubly-linked list with owned nodes. +/// +/// The `LinkedList` allows pushing and popping elements at either end +/// in constant time. +/// +/// Almost always it is better to use `Vec` or `VecDeque` instead of +/// `LinkedList`. In general, array-based containers are faster, +/// more memory efficient and make better use of CPU cache. #[stable(feature = "rust1", since = "1.0.0")] pub struct LinkedList<T> { head: Option<Shared<Node<T>>>, @@ -225,15 +239,17 @@ impl<T> LinkedList<T> { pub fn append(&mut self, other: &mut Self) { match self.tail { None => mem::swap(self, other), - Some(tail) => if let Some(other_head) = other.head.take() { - unsafe { - (**tail).next = Some(other_head); - (**other_head).prev = Some(tail); - } + Some(tail) => { + if let Some(other_head) = other.head.take() { + unsafe { + (**tail).next = Some(other_head); + (**other_head).prev = Some(tail); + } - self.tail = other.tail.take(); - self.len += mem::replace(&mut other.len, 0); - }, + self.tail = other.tail.take(); + self.len += mem::replace(&mut other.len, 0); + } + } } } @@ -674,7 +690,10 @@ impl<T> LinkedList<T> { reason = "method name and placement protocol are subject to change", issue = "30172")] pub fn front_place(&mut self) -> FrontPlace<T> { - FrontPlace { list: self, node: IntermediateBox::make_place() } + FrontPlace { + list: self, + node: IntermediateBox::make_place(), + } } /// Returns a place for insertion at the back of the list. @@ -699,7 +718,10 @@ impl<T> LinkedList<T> { reason = "method name and placement protocol are subject to change", issue = "30172")] pub fn back_place(&mut self) -> BackPlace<T> { - BackPlace { list: self, node: IntermediateBox::make_place() } + BackPlace { + list: self, + node: IntermediateBox::make_place(), + } } } @@ -852,7 +874,7 @@ impl<'a, T> IterMut<'a, T> { (**head).prev = node; self.list.len += 1; - } + }, } } @@ -1135,9 +1157,15 @@ impl<'a, T> InPlace<T> for BackPlace<'a, T> { // Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters. #[allow(dead_code)] fn assert_covariance() { - fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { x } - fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { x } - fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { x } + fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { + x + } + fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { + x + } + fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { + x + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1298,10 +1326,10 @@ mod tests { fn test_send() { let n = list_from(&[1, 2, 3]); thread::spawn(move || { - check_links(&n); - let a: &[_] = &[&1, &2, &3]; - assert_eq!(a, &n.iter().collect::<Vec<_>>()[..]); - }) + check_links(&n); + let a: &[_] = &[&1, &2, &3]; + assert_eq!(a, &n.iter().collect::<Vec<_>>()[..]); + }) .join() .ok() .unwrap(); diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index 5fb8cd6e1e2..b5e66692205 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -1496,10 +1496,10 @@ unsafe fn merge<T, F>(v: &mut [T], mid: usize, buf: *mut T, compare: &mut F) /// The algorithm identifies strictly descending and non-descending subsequences, which are called /// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed /// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are -/// satisfied, for every `i` in `0 .. runs.len() - 2`: +/// satisfied: /// -/// 1. `runs[i].len > runs[i + 1].len` -/// 2. `runs[i].len > runs[i + 1].len + runs[i + 2].len` +/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` +/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` /// /// The invariants ensure that the total running time is `O(n log n)` worst-case. fn merge_sort<T, F>(v: &mut [T], mut compare: F) diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index d4be0914f15..70cedce9a90 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -1697,11 +1697,7 @@ impl str { debug_assert!('Σ'.len_utf8() == 2); let is_word_final = case_ignoreable_then_cased(from[..i].chars().rev()) && !case_ignoreable_then_cased(from[i + 2..].chars()); - to.push_str(if is_word_final { - "ς" - } else { - "σ" - }); + to.push_str(if is_word_final { "ς" } else { "σ" }); } fn case_ignoreable_then_cased<I: Iterator<Item = char>>(iter: I) -> bool { diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index b4c41a99a6b..157c762b4a7 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -542,11 +542,7 @@ impl String { unsafe { *xs.get_unchecked(i) } } fn safe_get(xs: &[u8], i: usize, total: usize) -> u8 { - if i >= total { - 0 - } else { - unsafe_get(xs, i) - } + if i >= total { 0 } else { unsafe_get(xs, i) } } let mut res = String::with_capacity(total); @@ -976,7 +972,7 @@ impl String { pub fn push(&mut self, ch: char) { match ch.len_utf8() { 1 => self.vec.push(ch as u8), - _ => self.vec.extend_from_slice(ch.encode_utf8(&mut [0;4]).as_bytes()), + _ => self.vec.extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes()), } } @@ -1935,7 +1931,7 @@ impl<'a> FromIterator<String> for Cow<'a, str> { #[stable(feature = "from_string_for_vec_u8", since = "1.14.0")] impl From<String> for Vec<u8> { - fn from(string : String) -> Vec<u8> { + fn from(string: String) -> Vec<u8> { string.into_bytes() } } diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index c9f9e513ef3..f2ef54f6e56 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1902,14 +1902,13 @@ impl<T> IntoIter<T> { /// # Examples /// /// ``` - /// # #![feature(vec_into_iter_as_slice)] /// let vec = vec!['a', 'b', 'c']; /// let mut into_iter = vec.into_iter(); /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); /// let _ = into_iter.next().unwrap(); /// assert_eq!(into_iter.as_slice(), &['b', 'c']); /// ``` - #[unstable(feature = "vec_into_iter_as_slice", issue = "35601")] + #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] pub fn as_slice(&self) -> &[T] { unsafe { slice::from_raw_parts(self.ptr, self.len()) @@ -1921,7 +1920,6 @@ impl<T> IntoIter<T> { /// # Examples /// /// ``` - /// # #![feature(vec_into_iter_as_slice)] /// let vec = vec!['a', 'b', 'c']; /// let mut into_iter = vec.into_iter(); /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); @@ -1930,7 +1928,7 @@ impl<T> IntoIter<T> { /// assert_eq!(into_iter.next().unwrap(), 'b'); /// assert_eq!(into_iter.next().unwrap(), 'z'); /// ``` - #[unstable(feature = "vec_into_iter_as_slice", issue = "35601")] + #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] pub fn as_mut_slice(&self) -> &mut [T] { unsafe { slice::from_raw_parts_mut(self.ptr as *mut T, self.len()) diff --git a/src/libcollections/vec_deque.rs b/src/libcollections/vec_deque.rs index dbe3fec205c..67621b860bf 100644 --- a/src/libcollections/vec_deque.rs +++ b/src/libcollections/vec_deque.rs @@ -206,11 +206,7 @@ impl<T> VecDeque<T> { unsafe fn wrap_copy(&self, dst: usize, src: usize, len: usize) { #[allow(dead_code)] fn diff(a: usize, b: usize) -> usize { - if a <= b { - b - a - } else { - a - b - } + if a <= b { b - a } else { a - b } } debug_assert!(cmp::min(diff(dst, src), self.cap() - diff(dst, src)) + len <= self.cap(), "wrc dst={} src={} len={} cap={}", @@ -552,8 +548,8 @@ impl<T> VecDeque<T> { let old_cap = self.cap(); let used_cap = self.len() + 1; let new_cap = used_cap.checked_add(additional) - .and_then(|needed_cap| needed_cap.checked_next_power_of_two()) - .expect("capacity overflow"); + .and_then(|needed_cap| needed_cap.checked_next_power_of_two()) + .expect("capacity overflow"); if new_cap > self.capacity() { self.buf.reserve_exact(used_cap, new_cap - used_cap); @@ -1293,9 +1289,7 @@ impl<T> VecDeque<T> { let contiguous = self.is_contiguous(); - match (contiguous, - distance_to_tail <= distance_to_head, - idx >= self.tail) { + match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) { (true, true, _) if index == 0 => { // push_front // @@ -1513,9 +1507,7 @@ impl<T> VecDeque<T> { let contiguous = self.is_contiguous(); - match (contiguous, - distance_to_tail <= distance_to_head, - idx >= self.tail) { + match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) { (true, true, _) => { unsafe { // contiguous, remove closer to tail: @@ -1812,7 +1804,7 @@ fn wrap_index(index: usize, size: usize) -> usize { } /// Returns the two slices that cover the VecDeque's valid range -trait RingSlices : Sized { +trait RingSlices: Sized { fn slice(self, from: usize, to: usize) -> Self; fn split_at(self, i: usize) -> (Self, Self); @@ -1895,7 +1887,7 @@ impl<'a, T> Iterator for Iter<'a, T> { } fn fold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc - where F: FnMut(Acc, Self::Item) -> Acc, + where F: FnMut(Acc, Self::Item) -> Acc { let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); accum = front.iter().fold(accum, &mut f); @@ -1959,7 +1951,7 @@ impl<'a, T> Iterator for IterMut<'a, T> { } fn fold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc - where F: FnMut(Acc, Self::Item) -> Acc, + where F: FnMut(Acc, Self::Item) -> Acc { let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); accum = front.iter_mut().fold(accum, &mut f); @@ -2082,17 +2074,15 @@ impl<'a, T: 'a> Drop for Drain<'a, T> { (_, 0) => { source_deque.head = drain_tail; } - _ => { - unsafe { - if tail_len <= head_len { - source_deque.tail = source_deque.wrap_sub(drain_head, tail_len); - source_deque.wrap_copy(source_deque.tail, orig_tail, tail_len); - } else { - source_deque.head = source_deque.wrap_add(drain_tail, head_len); - source_deque.wrap_copy(drain_tail, drain_head, head_len); - } + _ => unsafe { + if tail_len <= head_len { + source_deque.tail = source_deque.wrap_sub(drain_head, tail_len); + source_deque.wrap_copy(source_deque.tail, orig_tail, tail_len); + } else { + source_deque.head = source_deque.wrap_add(drain_tail, head_len); + source_deque.wrap_copy(drain_tail, drain_head, head_len); } - } + }, } } } @@ -2288,10 +2278,8 @@ impl<T> From<Vec<T>> for VecDeque<T> { // We need to extend the buf if it's not a power of two, too small // or doesn't have at least one free space - if !buf.cap().is_power_of_two() - || (buf.cap() < (MINIMUM_CAPACITY + 1)) - || (buf.cap() == len) - { + if !buf.cap().is_power_of_two() || (buf.cap() < (MINIMUM_CAPACITY + 1)) || + (buf.cap() == len) { let cap = cmp::max(buf.cap() + 1, MINIMUM_CAPACITY + 1).next_power_of_two(); buf.reserve_exact(len, cap - len); } @@ -2299,7 +2287,7 @@ impl<T> From<Vec<T>> for VecDeque<T> { VecDeque { tail: 0, head: len, - buf: buf + buf: buf, } } } @@ -2324,18 +2312,17 @@ impl<T> From<VecDeque<T>> for Vec<T> { // do this in at most three copy moves. if (cap - tail) > head { // right hand block is the long one; move that enough for the left - ptr::copy( - buf.offset(tail as isize), - buf.offset((tail - head) as isize), - cap - tail); + ptr::copy(buf.offset(tail as isize), + buf.offset((tail - head) as isize), + cap - tail); // copy left in the end ptr::copy(buf, buf.offset((cap - head) as isize), head); // shift the new thing to the start - ptr::copy(buf.offset((tail-head) as isize), buf, len); + ptr::copy(buf.offset((tail - head) as isize), buf, len); } else { // left hand block is the long one, we can do it in two! - ptr::copy(buf, buf.offset((cap-tail) as isize), head); - ptr::copy(buf.offset(tail as isize), buf, cap-tail); + ptr::copy(buf, buf.offset((cap - tail) as isize), head); + ptr::copy(buf.offset(tail as isize), buf, cap - tail); } } else { // Need to use N swaps to move the ring @@ -2576,8 +2563,8 @@ mod tests { // We should see the correct values in the VecDeque let expected: VecDeque<_> = (0..drain_start) - .chain(drain_end..len) - .collect(); + .chain(drain_end..len) + .collect(); assert_eq!(expected, tester); } } @@ -2693,19 +2680,19 @@ mod tests { let cap = (2i32.pow(cap_pwr) - 1) as usize; // In these cases there is enough free space to solve it with copies - for len in 0..((cap+1)/2) { + for len in 0..((cap + 1) / 2) { // Test contiguous cases - for offset in 0..(cap-len) { + for offset in 0..(cap - len) { create_vec_and_test_convert(cap, offset, len) } // Test cases where block at end of buffer is bigger than block at start - for offset in (cap-len)..(cap-(len/2)) { + for offset in (cap - len)..(cap - (len / 2)) { create_vec_and_test_convert(cap, offset, len) } // Test cases where block at start of buffer is bigger than block at end - for offset in (cap-(len/2))..cap { + for offset in (cap - (len / 2))..cap { create_vec_and_test_convert(cap, offset, len) } } @@ -2714,19 +2701,19 @@ mod tests { // the ring will use swapping when: // (cap + 1 - offset) > (cap + 1 - len) && (len - (cap + 1 - offset)) > (cap + 1 - len)) // right block size > free space && left block size > free space - for len in ((cap+1)/2)..cap { + for len in ((cap + 1) / 2)..cap { // Test contiguous cases - for offset in 0..(cap-len) { + for offset in 0..(cap - len) { create_vec_and_test_convert(cap, offset, len) } // Test cases where block at end of buffer is bigger than block at start - for offset in (cap-len)..(cap-(len/2)) { + for offset in (cap - len)..(cap - (len / 2)) { create_vec_and_test_convert(cap, offset, len) } // Test cases where block at start of buffer is bigger than block at end - for offset in (cap-(len/2))..cap { + for offset in (cap - (len / 2))..cap { create_vec_and_test_convert(cap, offset, len) } } diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs index 0fe0a1bad64..d4fb5ea03ad 100644 --- a/src/libcollectionstest/lib.rs +++ b/src/libcollectionstest/lib.rs @@ -29,7 +29,6 @@ #![feature(test)] #![feature(unboxed_closures)] #![feature(unicode)] -#![feature(vec_into_iter_as_slice)] extern crate collections; extern crate test; diff --git a/src/libcompiler_builtins/build.rs b/src/libcompiler_builtins/build.rs index f61e2281a5c..44aa08e2458 100644 --- a/src/libcompiler_builtins/build.rs +++ b/src/libcompiler_builtins/build.rs @@ -242,7 +242,7 @@ fn main() { "atomic_thread_fence.c"]); } - if !target.contains("windows") { + if !target.contains("redox") && !target.contains("windows") { sources.extend(&["emutls.c"]); } diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 64a7a8c5ef7..c3f862e7c54 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -393,6 +393,8 @@ pub struct RefCell<T: ?Sized> { /// An enumeration of values returned from the `state` method on a `RefCell<T>`. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[unstable(feature = "borrow_state", issue = "27733")] +#[rustc_deprecated(since = "1.15.0", reason = "use `try_borrow` instead")] +#[allow(deprecated)] pub enum BorrowState { /// The cell is currently being read, there is at least one active `borrow`. Reading, @@ -511,6 +513,8 @@ impl<T: ?Sized> RefCell<T> { /// } /// ``` #[unstable(feature = "borrow_state", issue = "27733")] + #[rustc_deprecated(since = "1.15.0", reason = "use `try_borrow` instead")] + #[allow(deprecated)] #[inline] pub fn borrow_state(&self) -> BorrowState { match self.borrow.get() { @@ -888,9 +892,7 @@ impl<'b, T: ?Sized> Ref<'b, T> { /// `Ref::clone(...)`. A `Clone` implementation or a method would interfere /// with the widespread use of `r.borrow().clone()` to clone the contents of /// a `RefCell`. - #[unstable(feature = "cell_extras", - reason = "likely to be moved to a method, pending language changes", - issue = "27746")] + #[stable(feature = "cell_extras", since = "1.15.0")] #[inline] pub fn clone(orig: &Ref<'b, T>) -> Ref<'b, T> { Ref { diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 7f3ac13bac1..c14ae6e0898 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -327,9 +327,9 @@ pub trait CharExt { fn len_utf8(self) -> usize; #[stable(feature = "core", since = "1.6.0")] fn len_utf16(self) -> usize; - #[unstable(feature = "unicode", issue = "27784")] + #[stable(feature = "unicode_encode_char", since = "1.15.0")] fn encode_utf8(self, dst: &mut [u8]) -> &mut str; - #[unstable(feature = "unicode", issue = "27784")] + #[stable(feature = "unicode_encode_char", since = "1.15.0")] fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16]; } diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 9167264ba9d..2ba7d6e8bd1 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -12,7 +12,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use cell::{UnsafeCell, Cell, RefCell, Ref, RefMut, BorrowState}; +use cell::{UnsafeCell, Cell, RefCell, Ref, RefMut}; use marker::PhantomData; use mem; use num::flt2dec; @@ -1634,13 +1634,13 @@ impl<T: Copy + Debug> Debug for Cell<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized + Debug> Debug for RefCell<T> { fn fmt(&self, f: &mut Formatter) -> Result { - match self.borrow_state() { - BorrowState::Unused | BorrowState::Reading => { + match self.try_borrow() { + Ok(borrow) => { f.debug_struct("RefCell") - .field("value", &self.borrow()) + .field("value", &borrow) .finish() } - BorrowState::Writing => { + Err(_) => { f.debug_struct("RefCell") .field("value", &"<borrowed>") .finish() diff --git a/src/libcore/hash/mod.rs b/src/libcore/hash/mod.rs index ac36cbaace7..18b465d85a1 100644 --- a/src/libcore/hash/mod.rs +++ b/src/libcore/hash/mod.rs @@ -255,10 +255,44 @@ pub trait BuildHasher { fn build_hasher(&self) -> Self::Hasher; } -/// A structure which implements `BuildHasher` for all `Hasher` types which also -/// implement `Default`. +/// The `BuildHasherDefault` structure is used in scenarios where one has a +/// type that implements [`Hasher`] and [`Default`], but needs that type to +/// implement [`BuildHasher`]. /// -/// This struct is 0-sized and does not need construction. +/// This structure is zero-sized and does not need construction. +/// +/// # Examples +/// +/// Using `BuildHasherDefault` to specify a custom [`BuildHasher`] for +/// [`HashMap`]: +/// +/// ``` +/// use std::collections::HashMap; +/// use std::hash::{BuildHasherDefault, Hasher}; +/// +/// #[derive(Default)] +/// struct MyHasher; +/// +/// impl Hasher for MyHasher { +/// fn write(&mut self, bytes: &[u8]) { +/// // Your hashing algorithm goes here! +/// unimplemented!() +/// } +/// +/// fn finish(&self) -> u64 { +/// // Your hashing algorithm goes here! +/// unimplemented!() +/// } +/// } +/// +/// type MyBuildHasher = BuildHasherDefault<MyHasher>; +/// +/// let hash_map = HashMap::<u32, u32, MyBuildHasher>::default(); +/// ``` +/// +/// [`BuildHasher`]: trait.BuildHasher.html +/// [`Default`]: ../default/trait.Default.html +/// [`Hasher`]: trait.Hasher.html #[stable(since = "1.7.0", feature = "build_hasher")] pub struct BuildHasherDefault<H>(marker::PhantomData<H>); diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 48808b601c1..ec590d2bd06 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -1696,12 +1696,11 @@ pub trait Iterator { /// # Examples /// /// ``` - /// #![feature(iter_max_by)] /// let a = [-3_i32, 0, 1, 5, -10]; /// assert_eq!(*a.iter().max_by(|x, y| x.cmp(y)).unwrap(), 5); /// ``` #[inline] - #[unstable(feature = "iter_max_by", issue="36105")] + #[stable(feature = "iter_max_by", since = "1.15.0")] fn max_by<F>(self, mut compare: F) -> Option<Self::Item> where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { @@ -1746,12 +1745,11 @@ pub trait Iterator { /// # Examples /// /// ``` - /// #![feature(iter_min_by)] /// let a = [-3_i32, 0, 1, 5, -10]; /// assert_eq!(*a.iter().min_by(|x, y| x.cmp(y)).unwrap(), -10); /// ``` #[inline] - #[unstable(feature = "iter_min_by", issue="36105")] + #[stable(feature = "iter_min_by", since = "1.15.0")] fn min_by<F>(self, mut compare: F) -> Option<Self::Item> where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 2ad38de72b1..e3ca8eca76c 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -117,6 +117,8 @@ pub unsafe fn replace<T>(dest: *mut T, mut src: T) -> T { /// `zero_memory`, or `copy_memory`). Note that `*src = foo` counts as a use /// because it will attempt to drop the value previously at `*src`. /// +/// The pointer must be aligned; use `read_unaligned` if that is not the case. +/// /// # Examples /// /// Basic usage: @@ -137,6 +139,44 @@ pub unsafe fn read<T>(src: *const T) -> T { tmp } +/// Reads the value from `src` without moving it. This leaves the +/// memory in `src` unchanged. +/// +/// Unlike `read`, the pointer may be unaligned. +/// +/// # Safety +/// +/// Beyond accepting a raw pointer, this is unsafe because it semantically +/// moves the value out of `src` without preventing further usage of `src`. +/// If `T` is not `Copy`, then care must be taken to ensure that the value at +/// `src` is not used before the data is overwritten again (e.g. with `write`, +/// `zero_memory`, or `copy_memory`). Note that `*src = foo` counts as a use +/// because it will attempt to drop the value previously at `*src`. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// #![feature(ptr_unaligned)] +/// +/// let x = 12; +/// let y = &x as *const i32; +/// +/// unsafe { +/// assert_eq!(std::ptr::read_unaligned(y), 12); +/// } +/// ``` +#[inline(always)] +#[unstable(feature = "ptr_unaligned", issue = "37955")] +pub unsafe fn read_unaligned<T>(src: *const T) -> T { + let mut tmp: T = mem::uninitialized(); + copy_nonoverlapping(src as *const u8, + &mut tmp as *mut T as *mut u8, + mem::size_of::<T>()); + tmp +} + /// Overwrites a memory location with the given value without reading or /// dropping the old value. /// @@ -151,6 +191,8 @@ pub unsafe fn read<T>(src: *const T) -> T { /// This is appropriate for initializing uninitialized memory, or overwriting /// memory that has previously been `read` from. /// +/// The pointer must be aligned; use `write_unaligned` if that is not the case. +/// /// # Examples /// /// Basic usage: @@ -171,6 +213,47 @@ pub unsafe fn write<T>(dst: *mut T, src: T) { intrinsics::move_val_init(&mut *dst, src) } +/// Overwrites a memory location with the given value without reading or +/// dropping the old value. +/// +/// Unlike `write`, the pointer may be unaligned. +/// +/// # Safety +/// +/// This operation is marked unsafe because it accepts a raw pointer. +/// +/// It does not drop the contents of `dst`. This is safe, but it could leak +/// allocations or resources, so care must be taken not to overwrite an object +/// that should be dropped. +/// +/// This is appropriate for initializing uninitialized memory, or overwriting +/// memory that has previously been `read` from. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// #![feature(ptr_unaligned)] +/// +/// let mut x = 0; +/// let y = &mut x as *mut i32; +/// let z = 12; +/// +/// unsafe { +/// std::ptr::write_unaligned(y, z); +/// assert_eq!(std::ptr::read_unaligned(y), 12); +/// } +/// ``` +#[inline] +#[unstable(feature = "ptr_unaligned", issue = "37955")] +pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) { + copy_nonoverlapping(&src as *const T as *const u8, + dst as *mut u8, + mem::size_of::<T>()); + mem::forget(src); +} + /// Performs a volatile read of the value from `src` without moving it. This /// leaves the memory in `src` unchanged. /// diff --git a/src/libcore/result.rs b/src/libcore/result.rs index afed99d265f..99c407e5273 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -501,6 +501,8 @@ impl<T, E> Result<T, E> { /// Returns an iterator over the possibly contained value. /// + /// The iterator yields one value if the result is [`Ok`], otherwise none. + /// /// # Examples /// /// Basic usage: @@ -512,6 +514,8 @@ impl<T, E> Result<T, E> { /// let x: Result<u32, &str> = Err("nothing!"); /// assert_eq!(x.iter().next(), None); /// ``` + /// + /// [`Ok`]: enum.Result.html#variant.Ok #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn iter(&self) -> Iter<T> { @@ -520,6 +524,8 @@ impl<T, E> Result<T, E> { /// Returns a mutable iterator over the possibly contained value. /// + /// The iterator yields one value if the result is [`Ok`], otherwise none. + /// /// # Examples /// /// Basic usage: @@ -535,6 +541,8 @@ impl<T, E> Result<T, E> { /// let mut x: Result<u32, &str> = Err("nothing!"); /// assert_eq!(x.iter_mut().next(), None); /// ``` + /// + /// [`Ok`]: enum.Result.html#variant.Ok #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn iter_mut(&mut self) -> IterMut<T> { @@ -848,6 +856,8 @@ impl<T, E> IntoIterator for Result<T, E> { /// Returns a consuming iterator over the possibly contained value. /// + /// The iterator yields one value if the result is [`Ok`], otherwise none. + /// /// # Examples /// /// Basic usage: @@ -861,6 +871,8 @@ impl<T, E> IntoIterator for Result<T, E> { /// let v: Vec<u32> = x.into_iter().collect(); /// assert_eq!(v, []); /// ``` + /// + /// [`Ok`]: enum.Result.html#variant.Ok #[inline] fn into_iter(self) -> IntoIter<T> { IntoIter { inner: self.ok() } @@ -893,8 +905,13 @@ impl<'a, T, E> IntoIterator for &'a mut Result<T, E> { /// An iterator over a reference to the [`Ok`] variant of a [`Result`]. /// +/// The iterator yields one value if the result is [`Ok`], otherwise none. +/// +/// Created by [`Result::iter`]. +/// /// [`Ok`]: enum.Result.html#variant.Ok /// [`Result`]: enum.Result.html +/// [`Result::iter`]: enum.Result.html#method.iter #[derive(Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { inner: Option<&'a T> } @@ -934,8 +951,11 @@ impl<'a, T> Clone for Iter<'a, T> { /// An iterator over a mutable reference to the [`Ok`] variant of a [`Result`]. /// +/// Created by [`Result::iter_mut`]. +/// /// [`Ok`]: enum.Result.html#variant.Ok /// [`Result`]: enum.Result.html +/// [`Result::iter_mut`]: enum.Result.html#method.iter_mut #[derive(Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct IterMut<'a, T: 'a> { inner: Option<&'a mut T> } @@ -968,9 +988,12 @@ impl<'a, T> FusedIterator for IterMut<'a, T> {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl<'a, A> TrustedLen for IterMut<'a, A> {} -/// An iterator over the value in a [`Ok`] variant of a [`Result`]. This struct is -/// created by the [`into_iter`] method on [`Result`][`Result`] (provided by -/// the [`IntoIterator`] trait). +/// An iterator over the value in a [`Ok`] variant of a [`Result`]. +/// +/// The iterator yields one value if the result is [`Ok`], otherwise none. +/// +/// This struct is created by the [`into_iter`] method on +/// [`Result`][`Result`] (provided by the [`IntoIterator`] trait). /// /// [`Ok`]: enum.Result.html#variant.Ok /// [`Result`]: enum.Result.html diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index a4a90e7a9da..e0a49e2ae45 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -33,6 +33,7 @@ // * The `raw` and `bytes` submodules. // * Boilerplate trait implementations. +use borrow::Borrow; use cmp::Ordering::{self, Less, Equal, Greater}; use cmp; use fmt; @@ -100,15 +101,17 @@ pub trait SliceExt { #[stable(feature = "core", since = "1.6.0")] fn as_ptr(&self) -> *const Self::Item; #[stable(feature = "core", since = "1.6.0")] - fn binary_search(&self, x: &Self::Item) -> Result<usize, usize> - where Self::Item: Ord; + fn binary_search<Q: ?Sized>(&self, x: &Q) -> Result<usize, usize> + where Self::Item: Borrow<Q>, + Q: Ord; #[stable(feature = "core", since = "1.6.0")] fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize> where F: FnMut(&'a Self::Item) -> Ordering; #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] - fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result<usize, usize> + fn binary_search_by_key<'a, B, F, Q: ?Sized>(&'a self, b: &Q, f: F) -> Result<usize, usize> where F: FnMut(&'a Self::Item) -> B, - B: Ord; + B: Borrow<Q>, + Q: Ord; #[stable(feature = "core", since = "1.6.0")] fn len(&self) -> usize; #[stable(feature = "core", since = "1.6.0")] @@ -493,8 +496,8 @@ impl<T> SliceExt for [T] { m >= n && needle == &self[m-n..] } - fn binary_search(&self, x: &T) -> Result<usize, usize> where T: Ord { - self.binary_search_by(|p| p.cmp(x)) + fn binary_search<Q: ?Sized>(&self, x: &Q) -> Result<usize, usize> where T: Borrow<Q>, Q: Ord { + self.binary_search_by(|p| p.borrow().cmp(x)) } #[inline] @@ -522,11 +525,12 @@ impl<T> SliceExt for [T] { } #[inline] - fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result<usize, usize> + fn binary_search_by_key<'a, B, F, Q: ?Sized>(&'a self, b: &Q, mut f: F) -> Result<usize, usize> where F: FnMut(&'a Self::Item) -> B, - B: Ord + B: Borrow<Q>, + Q: Ord { - self.binary_search_by(|k| f(k).cmp(b)) + self.binary_search_by(|k| f(k).borrow().cmp(b)) } } diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index c10f7e39fc3..198db0e7c0a 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -203,7 +203,6 @@ impl AtomicBool { /// # Examples /// /// ``` - /// #![feature(atomic_access)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let mut some_bool = AtomicBool::new(true); @@ -212,7 +211,7 @@ impl AtomicBool { /// assert_eq!(some_bool.load(Ordering::SeqCst), false); /// ``` #[inline] - #[unstable(feature = "atomic_access", issue = "35603")] + #[stable(feature = "atomic_access", since = "1.15.0")] pub fn get_mut(&mut self) -> &mut bool { unsafe { &mut *(self.v.get() as *mut bool) } } @@ -225,14 +224,13 @@ impl AtomicBool { /// # Examples /// /// ``` - /// #![feature(atomic_access)] /// use std::sync::atomic::AtomicBool; /// /// let some_bool = AtomicBool::new(true); /// assert_eq!(some_bool.into_inner(), true); /// ``` #[inline] - #[unstable(feature = "atomic_access", issue = "35603")] + #[stable(feature = "atomic_access", since = "1.15.0")] pub fn into_inner(self) -> bool { unsafe { self.v.into_inner() != 0 } } @@ -588,7 +586,6 @@ impl<T> AtomicPtr<T> { /// # Examples /// /// ``` - /// #![feature(atomic_access)] /// use std::sync::atomic::{AtomicPtr, Ordering}; /// /// let mut atomic_ptr = AtomicPtr::new(&mut 10); @@ -596,7 +593,7 @@ impl<T> AtomicPtr<T> { /// assert_eq!(unsafe { *atomic_ptr.load(Ordering::SeqCst) }, 5); /// ``` #[inline] - #[unstable(feature = "atomic_access", issue = "35603")] + #[stable(feature = "atomic_access", since = "1.15.0")] pub fn get_mut(&mut self) -> &mut *mut T { unsafe { &mut *self.p.get() } } @@ -609,14 +606,13 @@ impl<T> AtomicPtr<T> { /// # Examples /// /// ``` - /// #![feature(atomic_access)] /// use std::sync::atomic::AtomicPtr; /// /// let atomic_ptr = AtomicPtr::new(&mut 5); /// assert_eq!(unsafe { *atomic_ptr.into_inner() }, 5); /// ``` #[inline] - #[unstable(feature = "atomic_access", issue = "35603")] + #[stable(feature = "atomic_access", since = "1.15.0")] pub fn into_inner(self) -> *mut T { unsafe { self.p.into_inner() } } @@ -883,7 +879,6 @@ macro_rules! atomic_int { /// # Examples /// /// ``` - /// #![feature(atomic_access)] /// use std::sync::atomic::{AtomicIsize, Ordering}; /// /// let mut some_isize = AtomicIsize::new(10); @@ -905,7 +900,6 @@ macro_rules! atomic_int { /// # Examples /// /// ``` - /// #![feature(atomic_access)] /// use std::sync::atomic::AtomicIsize; /// /// let some_isize = AtomicIsize::new(5); @@ -1261,7 +1255,7 @@ atomic_int!{ stable(feature = "rust1", since = "1.0.0"), stable(feature = "extended_compare_and_swap", since = "1.10.0"), stable(feature = "atomic_debug", since = "1.3.0"), - unstable(feature = "atomic_access", issue = "35603"), + stable(feature = "atomic_access", since = "1.15.0"), isize AtomicIsize ATOMIC_ISIZE_INIT } #[cfg(target_has_atomic = "ptr")] @@ -1269,7 +1263,7 @@ atomic_int!{ stable(feature = "rust1", since = "1.0.0"), stable(feature = "extended_compare_and_swap", since = "1.10.0"), stable(feature = "atomic_debug", since = "1.3.0"), - unstable(feature = "atomic_access", issue = "35603"), + stable(feature = "atomic_access", since = "1.15.0"), usize AtomicUsize ATOMIC_USIZE_INIT } diff --git a/src/libcoretest/cell.rs b/src/libcoretest/cell.rs index a7c230ba979..724a312ea79 100644 --- a/src/libcoretest/cell.rs +++ b/src/libcoretest/cell.rs @@ -59,22 +59,22 @@ fn double_imm_borrow() { fn no_mut_then_imm_borrow() { let x = RefCell::new(0); let _b1 = x.borrow_mut(); - assert_eq!(x.borrow_state(), BorrowState::Writing); + assert!(x.try_borrow().is_err()); } #[test] fn no_imm_then_borrow_mut() { let x = RefCell::new(0); let _b1 = x.borrow(); - assert_eq!(x.borrow_state(), BorrowState::Reading); + assert!(x.try_borrow_mut().is_err()); } #[test] fn no_double_borrow_mut() { let x = RefCell::new(0); - assert_eq!(x.borrow_state(), BorrowState::Unused); + assert!(x.try_borrow().is_ok()); let _b1 = x.borrow_mut(); - assert_eq!(x.borrow_state(), BorrowState::Writing); + assert!(x.try_borrow().is_err()); } #[test] @@ -102,7 +102,8 @@ fn double_borrow_single_release_no_borrow_mut() { { let _b2 = x.borrow(); } - assert_eq!(x.borrow_state(), BorrowState::Reading); + assert!(x.try_borrow().is_ok()); + assert!(x.try_borrow_mut().is_err()); } #[test] @@ -119,14 +120,18 @@ fn ref_clone_updates_flag() { let x = RefCell::new(0); { let b1 = x.borrow(); - assert_eq!(x.borrow_state(), BorrowState::Reading); + assert!(x.try_borrow().is_ok()); + assert!(x.try_borrow_mut().is_err()); { let _b2 = Ref::clone(&b1); - assert_eq!(x.borrow_state(), BorrowState::Reading); + assert!(x.try_borrow().is_ok()); + assert!(x.try_borrow_mut().is_err()); } - assert_eq!(x.borrow_state(), BorrowState::Reading); + assert!(x.try_borrow().is_ok()); + assert!(x.try_borrow_mut().is_err()); } - assert_eq!(x.borrow_state(), BorrowState::Unused); + assert!(x.try_borrow().is_ok()); + assert!(x.try_borrow_mut().is_ok()); } #[test] @@ -134,15 +139,19 @@ fn ref_map_does_not_update_flag() { let x = RefCell::new(Some(5)); { let b1: Ref<Option<u32>> = x.borrow(); - assert_eq!(x.borrow_state(), BorrowState::Reading); + assert!(x.try_borrow().is_ok()); + assert!(x.try_borrow_mut().is_err()); { let b2: Ref<u32> = Ref::map(b1, |o| o.as_ref().unwrap()); assert_eq!(*b2, 5); - assert_eq!(x.borrow_state(), BorrowState::Reading); + assert!(x.try_borrow().is_ok()); + assert!(x.try_borrow_mut().is_err()); } - assert_eq!(x.borrow_state(), BorrowState::Unused); + assert!(x.try_borrow().is_ok()); + assert!(x.try_borrow_mut().is_ok()); } - assert_eq!(x.borrow_state(), BorrowState::Unused); + assert!(x.try_borrow().is_ok()); + assert!(x.try_borrow_mut().is_ok()); } #[test] @@ -247,5 +256,3 @@ fn refcell_ref_coercion() { assert_eq!(&*coerced, comp); } } - - diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs index 92fb01e535c..d12616a97a6 100644 --- a/src/libcoretest/lib.rs +++ b/src/libcoretest/lib.rs @@ -10,9 +10,7 @@ #![deny(warnings)] -#![feature(borrow_state)] #![feature(box_syntax)] -#![feature(cell_extras)] #![feature(char_escape_debug)] #![feature(const_fn)] #![feature(core_private_bignum)] @@ -32,10 +30,9 @@ #![feature(try_from)] #![feature(unicode)] #![feature(unique)] -#![feature(iter_max_by)] -#![feature(iter_min_by)] #![feature(ordering_chaining)] #![feature(result_unwrap_or_default)] +#![feature(ptr_unaligned)] extern crate core; extern crate test; diff --git a/src/libcoretest/ptr.rs b/src/libcoretest/ptr.rs index f7fe61896f8..7f6f472bfbb 100644 --- a/src/libcoretest/ptr.rs +++ b/src/libcoretest/ptr.rs @@ -9,6 +9,7 @@ // except according to those terms. use core::ptr::*; +use core::cell::RefCell; #[test] fn test() { @@ -189,3 +190,25 @@ pub fn test_variadic_fnptr() { let mut s = SipHasher::new(); assert_eq!(p.hash(&mut s), q.hash(&mut s)); } + +#[test] +fn write_unaligned_drop() { + thread_local! { + static DROPS: RefCell<Vec<u32>> = RefCell::new(Vec::new()); + } + + struct Dropper(u32); + + impl Drop for Dropper { + fn drop(&mut self) { + DROPS.with(|d| d.borrow_mut().push(self.0)); + } + } + + { + let c = Dropper(0); + let mut t = Dropper(1); + unsafe { write_unaligned(&mut t, c); } + } + DROPS.with(|d| assert_eq!(*d.borrow(), [0])); +} diff --git a/src/liblibc b/src/liblibc -Subproject 0ac39c5ccf6a04395b7c40dd62321cb91f63f16 +Subproject e49e9bb7c3d9c7f2fd893f0ee0db81617b8db21 diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index 853f81ceaa9..8f85bfe2c63 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -28,7 +28,7 @@ #![panic_runtime] #![feature(panic_runtime)] #![cfg_attr(unix, feature(libc))] -#![cfg_attr(windows, feature(core_intrinsics))] +#![cfg_attr(any(target_os = "redox", windows), feature(core_intrinsics))] // Rust's "try" function, but if we're aborting on panics we just call the // function as there's nothing else we need to do here. @@ -61,7 +61,7 @@ pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 { libc::abort(); } - #[cfg(windows)] + #[cfg(any(target_os = "redox", windows))] unsafe fn abort() -> ! { core::intrinsics::abort(); } diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index ff483fa823e..b75d9ec6520 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -69,6 +69,7 @@ mod imp; // i686-pc-windows-gnu and all others #[cfg(any(all(unix, not(target_os = "emscripten")), + target_os = "redox", all(windows, target_arch = "x86", target_env = "gnu")))] #[path = "gcc.rs"] mod imp; diff --git a/src/librustc/dep_graph/README.md b/src/librustc/dep_graph/README.md index 48f5b7ea259..d2b94db689b 100644 --- a/src/librustc/dep_graph/README.md +++ b/src/librustc/dep_graph/README.md @@ -418,7 +418,7 @@ to see something like: Hir(foo) -> Collect(bar) Collect(bar) -> TypeckItemBody(bar) - + That first edge looks suspicious to you. So you set `RUST_FORBID_DEP_GRAPH_EDGE` to `Hir&foo -> Collect&bar`, re-run, and then observe the backtrace. Voila, bug fixed! @@ -440,6 +440,4 @@ To achieve this, the HIR map will detect if the def-id originates in an inlined node and add a dependency to a suitable `MetaData` node instead. If you are reading a HIR node and are not sure if it may be inlined or not, you can use `tcx.map.read(node_id)` and it will detect -whether the node is inlined or not and do the right thing. You can -also use `tcx.map.is_inlined_def_id()` and -`tcx.map.is_inlined_node_id()` to test. +whether the node is inlined or not and do the right thing. diff --git a/src/librustc/dep_graph/shadow.rs b/src/librustc/dep_graph/shadow.rs index 06def4bf19a..5d4190a8ae1 100644 --- a/src/librustc/dep_graph/shadow.rs +++ b/src/librustc/dep_graph/shadow.rs @@ -27,7 +27,7 @@ //! created. See `./README.md` for details. use hir::def_id::DefId; -use std::cell::{BorrowState, RefCell}; +use std::cell::RefCell; use std::env; use super::DepNode; @@ -71,15 +71,11 @@ impl ShadowGraph { pub fn enqueue(&self, message: &DepMessage) { if ENABLED { - match self.stack.borrow_state() { - BorrowState::Unused => {} - _ => { - // When we apply edge filters, that invokes the - // Debug trait on DefIds, which in turn reads from - // various bits of state and creates reads! Ignore - // those recursive reads. - return; - } + if self.stack.try_borrow().is_err() { + // When we apply edge filters, that invokes the Debug trait on + // DefIds, which in turn reads from various bits of state and + // creates reads! Ignore those recursive reads. + return; } let mut stack = self.stack.borrow_mut(); diff --git a/src/librustc/dep_graph/visit.rs b/src/librustc/dep_graph/visit.rs index 600732fc6f7..f6a22e47cf2 100644 --- a/src/librustc/dep_graph/visit.rs +++ b/src/librustc/dep_graph/visit.rs @@ -40,7 +40,6 @@ pub fn visit_all_item_likes_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx> let task_id = (self.dep_node_fn)(item_def_id); let _task = self.tcx.dep_graph.in_task(task_id.clone()); debug!("Started task {:?}", task_id); - assert!(!self.tcx.map.is_inlined_def_id(item_def_id)); self.tcx.dep_graph.read(DepNode::Hir(item_def_id)); self.visitor.visit_item(i); debug!("Ended task {:?}", task_id); @@ -51,7 +50,6 @@ pub fn visit_all_item_likes_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx> let task_id = (self.dep_node_fn)(impl_item_def_id); let _task = self.tcx.dep_graph.in_task(task_id.clone()); debug!("Started task {:?}", task_id); - assert!(!self.tcx.map.is_inlined_def_id(impl_item_def_id)); self.tcx.dep_graph.read(DepNode::Hir(impl_item_def_id)); self.visitor.visit_impl_item(i); debug!("Ended task {:?}", task_id); diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index ec09877ae12..1655c716b6b 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1236,6 +1236,23 @@ struct Foo<'a, T: 'a> { foo: &'a T } ``` + +To see why this is important, consider the case where `T` is itself a reference +(e.g., `T = &str`). If we don't include the restriction that `T: 'a`, the +following code would be perfectly legal: + +```compile_fail,E0309 +struct Foo<'a, T> { + foo: &'a T +} + +fn main() { + let v = "42".to_string(); + let f = Foo{foo: &v}; + drop(v); + println!("{}", f.foo); // but we've already dropped v! +} +``` "##, E0310: r##" diff --git a/src/librustc/hir/def_id.rs b/src/librustc/hir/def_id.rs index d3771b1755b..cbf162cc136 100644 --- a/src/librustc/hir/def_id.rs +++ b/src/librustc/hir/def_id.rs @@ -120,9 +120,7 @@ impl fmt::Debug for DefId { ty::tls::with_opt(|opt_tcx| { if let Some(tcx) = opt_tcx { - if let Some(def_path) = tcx.opt_def_path(*self) { - write!(f, " => {}", def_path.to_string(tcx))?; - } + write!(f, " => {}", tcx.def_path(*self).to_string(tcx))?; } Ok(()) })?; diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 625bde2ca8b..186d6f62650 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -365,7 +365,6 @@ pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate) { pub fn walk_macro_def<'v, V: Visitor<'v>>(visitor: &mut V, macro_def: &'v MacroDef) { visitor.visit_id(macro_def.id); visitor.visit_name(macro_def.span, macro_def.name); - walk_opt_name(visitor, macro_def.span, macro_def.imported_from); walk_list!(visitor, visit_attribute, ¯o_def.attrs); } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 74876eb59ee..1cf5e35a095 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -81,7 +81,7 @@ pub struct LoweringContext<'a> { } pub trait Resolver { - // Resolve a global hir path generated by the lowerer when expanding `for`, `if let`, etc. + // Resolve a hir path generated by the lowerer when expanding `for`, `if let`, etc. fn resolve_hir_path(&mut self, path: &mut hir::Path, is_value: bool); // Obtain the resolution for a node id @@ -337,7 +337,6 @@ impl<'a> LoweringContext<'a> { let proj_start = p.segments.len() - resolution.depth; let path = P(hir::Path { - global: p.global, def: resolution.base_def, segments: p.segments[..proj_start].iter().enumerate().map(|(i, segment)| { let param_mode = match (qself_position, param_mode) { @@ -404,12 +403,17 @@ impl<'a> LoweringContext<'a> { id: NodeId, p: &Path, name: Option<Name>, - param_mode: ParamMode) + param_mode: ParamMode, + defaults_to_global: bool) -> hir::Path { + let mut segments = p.segments.iter(); + if defaults_to_global && p.is_global() { + segments.next(); + } + hir::Path { - global: p.global, def: self.expect_full_def(id), - segments: p.segments.iter().map(|segment| { + segments: segments.map(|segment| { self.lower_path_segment(segment, param_mode) }).chain(name.map(|name| { hir::PathSegment { @@ -424,22 +428,29 @@ impl<'a> LoweringContext<'a> { fn lower_path(&mut self, id: NodeId, p: &Path, - param_mode: ParamMode) + param_mode: ParamMode, + defaults_to_global: bool) -> hir::Path { - self.lower_path_extra(id, p, None, param_mode) + self.lower_path_extra(id, p, None, param_mode, defaults_to_global) } fn lower_path_segment(&mut self, segment: &PathSegment, param_mode: ParamMode) -> hir::PathSegment { - let parameters = match segment.parameters { - PathParameters::AngleBracketed(ref data) => { - let data = self.lower_angle_bracketed_parameter_data(data, param_mode); - hir::AngleBracketedParameters(data) + let parameters = if let Some(ref parameters) = segment.parameters { + match **parameters { + PathParameters::AngleBracketed(ref data) => { + let data = self.lower_angle_bracketed_parameter_data(data, param_mode); + hir::AngleBracketedParameters(data) + } + PathParameters::Parenthesized(ref data) => { + hir::ParenthesizedParameters(self.lower_parenthesized_parameter_data(data)) + } } - PathParameters::Parenthesized(ref data) => - hir::ParenthesizedParameters(self.lower_parenthesized_parameter_data(data)), + } else { + let data = self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode); + hir::AngleBracketedParameters(data) }; hir::PathSegment { @@ -596,8 +607,8 @@ impl<'a> LoweringContext<'a> { // Check if the where clause type is a plain type parameter. match bound_pred.bounded_ty.node { TyKind::Path(None, ref path) - if !path.global && path.segments.len() == 1 && - bound_pred.bound_lifetimes.is_empty() => { + if path.segments.len() == 1 && + bound_pred.bound_lifetimes.is_empty() => { if let Some(Def::TyParam(def_id)) = self.resolver.get_resolution(bound_pred.bounded_ty.id) .map(|d| d.base_def) { @@ -671,7 +682,7 @@ impl<'a> LoweringContext<'a> { span}) => { hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { id: id, - path: self.lower_path(id, path, ParamMode::Explicit), + path: self.lower_path(id, path, ParamMode::Explicit, false), ty: self.lower_ty(ty), span: span, }) @@ -701,7 +712,7 @@ impl<'a> LoweringContext<'a> { fn lower_trait_ref(&mut self, p: &TraitRef) -> hir::TraitRef { hir::TraitRef { - path: self.lower_path(p.ref_id, &p.path, ParamMode::Explicit), + path: self.lower_path(p.ref_id, &p.path, ParamMode::Explicit, false), ref_id: p.ref_id, } } @@ -794,7 +805,7 @@ impl<'a> LoweringContext<'a> { }; let mut path = self.lower_path_extra(import.id, path, suffix, - ParamMode::Explicit); + ParamMode::Explicit, true); path.span = span; self.items.insert(import.id, hir::Item { id: import.id, @@ -808,7 +819,7 @@ impl<'a> LoweringContext<'a> { path } }; - let path = P(self.lower_path(id, path, ParamMode::Explicit)); + let path = P(self.lower_path(id, path, ParamMode::Explicit, true)); let kind = match view_path.node { ViewPathSimple(ident, _) => { *name = ident.name; @@ -987,8 +998,6 @@ impl<'a> LoweringContext<'a> { attrs: self.lower_attrs(&m.attrs), id: m.id, span: m.span, - imported_from: m.imported_from.map(|x| x.name), - allow_internal_unstable: m.allow_internal_unstable, body: m.body.clone().into(), } } @@ -1131,7 +1140,6 @@ impl<'a> LoweringContext<'a> { Some(def) => { hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path { span: pth1.span, - global: false, def: def, segments: hir_vec![ hir::PathSegment::from_name(pth1.node.name) @@ -1874,7 +1882,7 @@ impl<'a> LoweringContext<'a> { Visibility::Crate(_) => hir::Visibility::Crate, Visibility::Restricted { ref path, id } => { hir::Visibility::Restricted { - path: P(self.lower_path(id, path, ParamMode::Explicit)), + path: P(self.lower_path(id, path, ParamMode::Explicit, true)), id: id } } @@ -1967,7 +1975,6 @@ impl<'a> LoweringContext<'a> { let expr_path = hir::ExprPath(hir::QPath::Resolved(None, P(hir::Path { span: span, - global: false, def: def, segments: hir_vec![hir::PathSegment::from_name(id)], }))); @@ -2135,17 +2142,12 @@ impl<'a> LoweringContext<'a> { /// `fld.cx.use_std`, and `::core::b::c::d` otherwise. /// The path is also resolved according to `is_value`. fn std_path(&mut self, span: Span, components: &[&str], is_value: bool) -> hir::Path { - let idents = self.crate_root.iter().chain(components); - - let segments: Vec<_> = idents.map(|name| { - hir::PathSegment::from_name(Symbol::intern(name)) - }).collect(); - let mut path = hir::Path { span: span, - global: true, def: Def::Err, - segments: segments.into(), + segments: iter::once(keywords::CrateRoot.name()).chain({ + self.crate_root.into_iter().chain(components.iter().cloned()).map(Symbol::intern) + }).map(hir::PathSegment::from_name).collect(), }; self.resolver.resolve_hir_path(&mut path, is_value); diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs index c46c8f044e0..45988886a60 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc/hir/map/collector.rs @@ -11,7 +11,6 @@ use super::*; use hir::intravisit::{Visitor, NestedVisitorMap}; -use hir::def_id::DefId; use middle::cstore::InlinedItem; use std::iter::repeat; use syntax::ast::{NodeId, CRATE_NODE_ID}; @@ -47,8 +46,6 @@ impl<'ast> NodeCollector<'ast> { pub fn extend(krate: &'ast Crate, parent: &'ast InlinedItem, parent_node: NodeId, - parent_def_path: DefPath, - parent_def_id: DefId, map: Vec<MapEntry<'ast>>) -> NodeCollector<'ast> { let mut collector = NodeCollector { @@ -58,7 +55,6 @@ impl<'ast> NodeCollector<'ast> { ignore_nested_items: true }; - assert_eq!(parent_def_path.krate, parent_def_id.krate); collector.insert_entry(parent_node, RootInlinedParent(parent)); collector diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index eb5a89f320e..256aee342a3 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -9,12 +9,7 @@ // except according to those terms. use hir::map::definitions::*; - -use hir; -use hir::intravisit::{self, Visitor, NestedVisitorMap}; -use hir::def_id::{CRATE_DEF_INDEX, DefId, DefIndex}; - -use middle::cstore::InlinedItem; +use hir::def_id::{CRATE_DEF_INDEX, DefIndex}; use syntax::ast::*; use syntax::ext::hygiene::Mark; @@ -23,9 +18,6 @@ use syntax::symbol::{Symbol, keywords}; /// Creates def ids for nodes in the HIR. pub struct DefCollector<'a> { - // If we are walking HIR (c.f., AST), we need to keep a reference to the - // crate. - hir_crate: Option<&'a hir::Crate>, definitions: &'a mut Definitions, parent_def: Option<DefIndex>, pub visit_macro_invoc: Option<&'a mut FnMut(MacroInvocationData)>, @@ -40,43 +32,16 @@ pub struct MacroInvocationData { impl<'a> DefCollector<'a> { pub fn new(definitions: &'a mut Definitions) -> Self { DefCollector { - hir_crate: None, definitions: definitions, parent_def: None, visit_macro_invoc: None, } } - pub fn extend(parent_node: NodeId, - parent_def_path: DefPath, - parent_def_id: DefId, - definitions: &'a mut Definitions) - -> Self { - let mut collector = DefCollector::new(definitions); - - assert_eq!(parent_def_path.krate, parent_def_id.krate); - let root_path = Box::new(InlinedRootPath { - data: parent_def_path.data, - def_id: parent_def_id, - }); - - let def = collector.create_def(parent_node, DefPathData::InlinedRoot(root_path)); - collector.parent_def = Some(def); - - collector - } - pub fn collect_root(&mut self) { let root = self.create_def_with_parent(None, CRATE_NODE_ID, DefPathData::CrateRoot); assert_eq!(root, CRATE_DEF_INDEX); self.parent_def = Some(root); - - self.create_def_with_parent(Some(CRATE_DEF_INDEX), DUMMY_NODE_ID, DefPathData::Misc); - } - - pub fn walk_item(&mut self, ii: &'a InlinedItem, krate: &'a hir::Crate) { - self.hir_crate = Some(krate); - ii.visit(self); } fn create_def(&mut self, node_id: NodeId, data: DefPathData) -> DefIndex { @@ -114,16 +79,6 @@ impl<'a> DefCollector<'a> { self.create_def(expr.id, DefPathData::Initializer); } - fn visit_hir_const_integer(&mut self, expr: &hir::Expr) { - // FIXME(eddyb) Closures should have separate - // function definition IDs and expression IDs. - if let hir::ExprClosure(..) = expr.node { - return; - } - - self.create_def(expr.id, DefPathData::Initializer); - } - fn visit_macro_invoc(&mut self, id: NodeId, const_integer: bool) { if let Some(ref mut visit) = self.visit_macro_invoc { visit(MacroInvocationData { @@ -324,169 +279,3 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { } } } - -// We walk the HIR rather than the AST when reading items from metadata. -impl<'ast> Visitor<'ast> for DefCollector<'ast> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'ast> { - // note however that we override `visit_body` below - NestedVisitorMap::None - } - - fn visit_body(&mut self, id: hir::ExprId) { - if let Some(krate) = self.hir_crate { - self.visit_expr(krate.expr(id)); - } - } - - fn visit_item(&mut self, i: &'ast hir::Item) { - debug!("visit_item: {:?}", i); - - // Pick the def data. This need not be unique, but the more - // information we encapsulate into - let def_data = match i.node { - hir::ItemDefaultImpl(..) | hir::ItemImpl(..) => - DefPathData::Impl, - hir::ItemEnum(..) | hir::ItemStruct(..) | hir::ItemUnion(..) | - hir::ItemTrait(..) | hir::ItemExternCrate(..) | hir::ItemMod(..) | - hir::ItemForeignMod(..) | hir::ItemTy(..) => - DefPathData::TypeNs(i.name.as_str()), - hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => - DefPathData::ValueNs(i.name.as_str()), - hir::ItemUse(..) => DefPathData::Misc, - }; - let def = self.create_def(i.id, def_data); - - self.with_parent(def, |this| { - match i.node { - hir::ItemEnum(ref enum_definition, _) => { - for v in &enum_definition.variants { - let variant_def_index = - this.create_def(v.node.data.id(), - DefPathData::EnumVariant(v.node.name.as_str())); - - this.with_parent(variant_def_index, |this| { - for field in v.node.data.fields() { - this.create_def(field.id, - DefPathData::Field(field.name.as_str())); - } - if let Some(ref expr) = v.node.disr_expr { - this.visit_hir_const_integer(expr); - } - }); - } - } - hir::ItemStruct(ref struct_def, _) | - hir::ItemUnion(ref struct_def, _) => { - // If this is a tuple-like struct, register the constructor. - if !struct_def.is_struct() { - this.create_def(struct_def.id(), - DefPathData::StructCtor); - } - - for field in struct_def.fields() { - this.create_def(field.id, DefPathData::Field(field.name.as_str())); - } - } - _ => {} - } - intravisit::walk_item(this, i); - }); - } - - fn visit_foreign_item(&mut self, foreign_item: &'ast hir::ForeignItem) { - let def = self.create_def(foreign_item.id, - DefPathData::ValueNs(foreign_item.name.as_str())); - - self.with_parent(def, |this| { - intravisit::walk_foreign_item(this, foreign_item); - }); - } - - fn visit_generics(&mut self, generics: &'ast hir::Generics) { - for ty_param in generics.ty_params.iter() { - self.create_def(ty_param.id, DefPathData::TypeParam(ty_param.name.as_str())); - } - - intravisit::walk_generics(self, generics); - } - - fn visit_trait_item(&mut self, ti: &'ast hir::TraitItem) { - let def_data = match ti.node { - hir::MethodTraitItem(..) | hir::ConstTraitItem(..) => - DefPathData::ValueNs(ti.name.as_str()), - hir::TypeTraitItem(..) => DefPathData::TypeNs(ti.name.as_str()), - }; - - let def = self.create_def(ti.id, def_data); - self.with_parent(def, |this| { - if let hir::ConstTraitItem(_, Some(ref expr)) = ti.node { - this.create_def(expr.id, DefPathData::Initializer); - } - - intravisit::walk_trait_item(this, ti); - }); - } - - fn visit_impl_item(&mut self, ii: &'ast hir::ImplItem) { - let def_data = match ii.node { - hir::ImplItemKind::Method(..) | hir::ImplItemKind::Const(..) => - DefPathData::ValueNs(ii.name.as_str()), - hir::ImplItemKind::Type(..) => DefPathData::TypeNs(ii.name.as_str()), - }; - - let def = self.create_def(ii.id, def_data); - self.with_parent(def, |this| { - if let hir::ImplItemKind::Const(_, ref expr) = ii.node { - this.create_def(expr.id, DefPathData::Initializer); - } - - intravisit::walk_impl_item(this, ii); - }); - } - - fn visit_pat(&mut self, pat: &'ast hir::Pat) { - let parent_def = self.parent_def; - - if let hir::PatKind::Binding(_, _, name, _) = pat.node { - let def = self.create_def(pat.id, DefPathData::Binding(name.node.as_str())); - self.parent_def = Some(def); - } - - intravisit::walk_pat(self, pat); - self.parent_def = parent_def; - } - - fn visit_expr(&mut self, expr: &'ast hir::Expr) { - let parent_def = self.parent_def; - - if let hir::ExprRepeat(_, ref count) = expr.node { - self.visit_hir_const_integer(count); - } - - if let hir::ExprClosure(..) = expr.node { - let def = self.create_def(expr.id, DefPathData::ClosureExpr); - self.parent_def = Some(def); - } - - intravisit::walk_expr(self, expr); - self.parent_def = parent_def; - } - - fn visit_ty(&mut self, ty: &'ast hir::Ty) { - if let hir::TyArray(_, ref length) = ty.node { - self.visit_hir_const_integer(length); - } - if let hir::TyImplTrait(..) = ty.node { - self.create_def(ty.id, DefPathData::ImplTrait); - } - intravisit::walk_ty(self, ty); - } - - fn visit_lifetime_def(&mut self, def: &'ast hir::LifetimeDef) { - self.create_def(def.lifetime.id, DefPathData::LifetimeDef(def.lifetime.name.as_str())); - } - - fn visit_macro_def(&mut self, macro_def: &'ast hir::MacroDef) { - self.create_def(macro_def.id, DefPathData::MacroDef(macro_def.name.as_str())); - } -} diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs index 83d3627d8e6..4f64670f482 100644 --- a/src/librustc/hir/map/definitions.rs +++ b/src/librustc/hir/map/definitions.rs @@ -8,22 +8,119 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! For each definition, we track the following data. A definition +//! here is defined somewhat circularly as "something with a def-id", +//! but it generally corresponds to things like structs, enums, etc. +//! There are also some rather random cases (like const initializer +//! expressions) that are mostly just leftovers. + use hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::StableHasher; +use serialize::{Encodable, Decodable, Encoder, Decoder}; use std::fmt::Write; use std::hash::{Hash, Hasher}; -use std::collections::hash_map::DefaultHasher; use syntax::ast; use syntax::symbol::{Symbol, InternedString}; use ty::TyCtxt; use util::nodemap::NodeMap; -/// The definition table containing node definitions +/// The DefPathTable maps DefIndexes to DefKeys and vice versa. +/// Internally the DefPathTable holds a tree of DefKeys, where each DefKey +/// stores the DefIndex of its parent. +/// There is one DefPathTable for each crate. +#[derive(Clone)] +pub struct DefPathTable { + index_to_key: Vec<DefKey>, + key_to_index: FxHashMap<DefKey, DefIndex>, +} + +impl DefPathTable { + fn insert(&mut self, key: DefKey) -> DefIndex { + let index = DefIndex::new(self.index_to_key.len()); + debug!("DefPathTable::insert() - {:?} <-> {:?}", key, index); + self.index_to_key.push(key.clone()); + self.key_to_index.insert(key, index); + index + } + + #[inline(always)] + pub fn def_key(&self, index: DefIndex) -> DefKey { + self.index_to_key[index.as_usize()].clone() + } + + #[inline(always)] + pub fn def_index_for_def_key(&self, key: &DefKey) -> Option<DefIndex> { + self.key_to_index.get(key).cloned() + } + + #[inline(always)] + pub fn contains_key(&self, key: &DefKey) -> bool { + self.key_to_index.contains_key(key) + } + + pub fn retrace_path(&self, + path_data: &[DisambiguatedDefPathData]) + -> Option<DefIndex> { + let root_key = DefKey { + parent: None, + disambiguated_data: DisambiguatedDefPathData { + data: DefPathData::CrateRoot, + disambiguator: 0, + }, + }; + + let root_index = self.key_to_index + .get(&root_key) + .expect("no root key?") + .clone(); + + debug!("retrace_path: root_index={:?}", root_index); + + let mut index = root_index; + for data in path_data { + let key = DefKey { parent: Some(index), disambiguated_data: data.clone() }; + debug!("retrace_path: key={:?}", key); + match self.key_to_index.get(&key) { + Some(&i) => index = i, + None => return None, + } + } + + Some(index) + } +} + + +impl Encodable for DefPathTable { + fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { + self.index_to_key.encode(s) + } +} + +impl Decodable for DefPathTable { + fn decode<D: Decoder>(d: &mut D) -> Result<DefPathTable, D::Error> { + let index_to_key: Vec<DefKey> = Decodable::decode(d)?; + let key_to_index = index_to_key.iter() + .enumerate() + .map(|(index, key)| (key.clone(), DefIndex::new(index))) + .collect(); + Ok(DefPathTable { + index_to_key: index_to_key, + key_to_index: key_to_index, + }) + } +} + + +/// The definition table containing node definitions. +/// It holds the DefPathTable for local DefIds/DefPaths and it also stores a +/// mapping from NodeIds to local DefIds. #[derive(Clone)] pub struct Definitions { - data: Vec<DefData>, - key_map: FxHashMap<DefKey, DefIndex>, - node_map: NodeMap<DefIndex>, + table: DefPathTable, + node_to_def_index: NodeMap<DefIndex>, + def_index_to_node: Vec<ast::NodeId>, } /// A unique identifier that we can use to lookup a definition @@ -50,19 +147,6 @@ pub struct DisambiguatedDefPathData { pub disambiguator: u32 } -/// For each definition, we track the following data. A definition -/// here is defined somewhat circularly as "something with a def-id", -/// but it generally corresponds to things like structs, enums, etc. -/// There are also some rather random cases (like const initializer -/// expressions) that are mostly just leftovers. -#[derive(Clone, Debug)] -pub struct DefData { - pub key: DefKey, - - /// Local ID within the HIR. - pub node_id: ast::NodeId, -} - #[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct DefPath { /// the path leading from the crate root to the item @@ -77,12 +161,11 @@ impl DefPath { self.krate == LOCAL_CRATE } - pub fn make<FN>(start_krate: CrateNum, + pub fn make<FN>(krate: CrateNum, start_index: DefIndex, mut get_key: FN) -> DefPath where FN: FnMut(DefIndex) -> DefKey { - let mut krate = start_krate; let mut data = vec![]; let mut index = Some(start_index); loop { @@ -95,13 +178,6 @@ impl DefPath { assert!(key.parent.is_none()); break; } - DefPathData::InlinedRoot(ref p) => { - assert!(key.parent.is_none()); - assert!(!p.def_id.is_local()); - data.extend(p.data.iter().cloned().rev()); - krate = p.def_id.krate; - break; - } _ => { data.push(key.disambiguated_data); index = key.parent; @@ -131,7 +207,8 @@ impl DefPath { } pub fn deterministic_hash(&self, tcx: TyCtxt) -> u64 { - let mut state = DefaultHasher::new(); + debug!("deterministic_hash({:?})", self); + let mut state = StableHasher::new(); self.deterministic_hash_to(tcx, &mut state); state.finish() } @@ -143,31 +220,6 @@ impl DefPath { } } -/// Root of an inlined item. We track the `DefPath` of the item within -/// the original crate but also its def-id. This is kind of an -/// augmented version of a `DefPath` that includes a `DefId`. This is -/// all sort of ugly but the hope is that inlined items will be going -/// away soon anyway. -/// -/// Some of the constraints that led to the current approach: -/// -/// - I don't want to have a `DefId` in the main `DefPath` because -/// that gets serialized for incr. comp., and when reloaded the -/// `DefId` is no longer valid. I'd rather maintain the invariant -/// that every `DefId` is valid, and a potentially outdated `DefId` is -/// represented as a `DefPath`. -/// - (We don't serialize def-paths from inlined items, so it's ok to have one here.) -/// - We need to be able to extract the def-id from inline items to -/// make the symbol name. In theory we could retrace it from the -/// data, but the metadata doesn't have the required indices, and I -/// don't want to write the code to create one just for this. -/// - It may be that we don't actually need `data` at all. We'll have -/// to see about that. -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -pub struct InlinedRootPath { - pub data: Vec<DisambiguatedDefPathData>, - pub def_id: DefId, -} #[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum DefPathData { @@ -175,8 +227,6 @@ pub enum DefPathData { // they are treated specially by the `def_path` function. /// The crate root (marker) CrateRoot, - /// An inlined root - InlinedRoot(Box<InlinedRootPath>), // Catch-all for random DefId things like DUMMY_NODE_ID Misc, @@ -218,23 +268,30 @@ impl Definitions { /// Create new empty definition map. pub fn new() -> Definitions { Definitions { - data: vec![], - key_map: FxHashMap(), - node_map: NodeMap(), + table: DefPathTable { + index_to_key: vec![], + key_to_index: FxHashMap(), + }, + node_to_def_index: NodeMap(), + def_index_to_node: vec![], } } + pub fn def_path_table(&self) -> &DefPathTable { + &self.table + } + /// Get the number of definitions. pub fn len(&self) -> usize { - self.data.len() + self.def_index_to_node.len() } pub fn def_key(&self, index: DefIndex) -> DefKey { - self.data[index.as_usize()].key.clone() + self.table.def_key(index) } pub fn def_index_for_def_key(&self, key: DefKey) -> Option<DefIndex> { - self.key_map.get(&key).cloned() + self.table.def_index_for_def_key(&key) } /// Returns the path from the crate root to `index`. The root @@ -247,7 +304,7 @@ impl Definitions { } pub fn opt_def_index(&self, node: ast::NodeId) -> Option<DefIndex> { - self.node_map.get(&node).cloned() + self.node_to_def_index.get(&node).cloned() } pub fn opt_local_def_id(&self, node: ast::NodeId) -> Option<DefId> { @@ -260,8 +317,8 @@ impl Definitions { pub fn as_local_node_id(&self, def_id: DefId) -> Option<ast::NodeId> { if def_id.krate == LOCAL_CRATE { - assert!(def_id.index.as_usize() < self.data.len()); - Some(self.data[def_id.index.as_usize()].node_id) + assert!(def_id.index.as_usize() < self.def_index_to_node.len()); + Some(self.def_index_to_node[def_id.index.as_usize()]) } else { None } @@ -276,16 +333,13 @@ impl Definitions { debug!("create_def_with_parent(parent={:?}, node_id={:?}, data={:?})", parent, node_id, data); - assert!(!self.node_map.contains_key(&node_id), + assert!(!self.node_to_def_index.contains_key(&node_id), "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}", node_id, data, - self.data[self.node_map[&node_id].as_usize()]); + self.table.def_key(self.node_to_def_index[&node_id])); - assert!(parent.is_some() ^ match data { - DefPathData::CrateRoot | DefPathData::InlinedRoot(_) => true, - _ => false, - }); + assert!(parent.is_some() ^ (data == DefPathData::CrateRoot)); // Find a unique DefKey. This basically means incrementing the disambiguator // until we get no match. @@ -297,20 +351,18 @@ impl Definitions { } }; - while self.key_map.contains_key(&key) { + while self.table.contains_key(&key) { key.disambiguated_data.disambiguator += 1; } debug!("create_def_with_parent: after disambiguation, key = {:?}", key); // Create the definition. - let index = DefIndex::new(self.data.len()); - self.data.push(DefData { key: key.clone(), node_id: node_id }); - debug!("create_def_with_parent: node_map[{:?}] = {:?}", node_id, index); - self.node_map.insert(node_id, index); - debug!("create_def_with_parent: key_map[{:?}] = {:?}", key, index); - self.key_map.insert(key, index); - + let index = self.table.insert(key); + debug!("create_def_with_parent: def_index_to_node[{:?} <-> {:?}", index, node_id); + self.node_to_def_index.insert(node_id, index); + assert_eq!(index.as_usize(), self.def_index_to_node.len()); + self.def_index_to_node.push(node_id); index } @@ -332,7 +384,6 @@ impl DefPathData { Impl | CrateRoot | - InlinedRoot(_) | Misc | ClosureExpr | StructCtor | @@ -359,9 +410,6 @@ impl DefPathData { // note that this does not show up in user printouts CrateRoot => "{{root}}", - // note that this does not show up in user printouts - InlinedRoot(_) => "{{inlined-root}}", - Impl => "{{impl}}", Misc => "{{?}}", ClosureExpr => "{{closure}}", @@ -377,4 +425,3 @@ impl DefPathData { self.as_interned_str().to_string() } } - diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 117edcf14a1..4546f6d8c27 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -13,7 +13,7 @@ use self::MapEntry::*; use self::collector::NodeCollector; pub use self::def_collector::{DefCollector, MacroInvocationData}; pub use self::definitions::{Definitions, DefKey, DefPath, DefPathData, - DisambiguatedDefPathData, InlinedRootPath}; + DisambiguatedDefPathData}; use dep_graph::{DepGraph, DepNode}; @@ -221,22 +221,14 @@ pub struct Map<'ast> { /// plain old integers. map: RefCell<Vec<MapEntry<'ast>>>, - definitions: RefCell<Definitions>, + definitions: Definitions, /// All NodeIds that are numerically greater or equal to this value come /// from inlined items. local_node_id_watermark: NodeId, - - /// All def-indices that are numerically greater or equal to this value come - /// from inlined items. - local_def_id_watermark: usize, } impl<'ast> Map<'ast> { - pub fn is_inlined_def_id(&self, id: DefId) -> bool { - id.is_local() && id.index.as_usize() >= self.local_def_id_watermark - } - pub fn is_inlined_node_id(&self, id: NodeId) -> bool { id >= self.local_node_id_watermark } @@ -262,7 +254,6 @@ impl<'ast> Map<'ast> { EntryItem(_, item) => { assert_eq!(id, item.id); let def_id = self.local_def_id(id); - assert!(!self.is_inlined_def_id(def_id)); if let Some(last_id) = last_expr { // The body of the item may have a separate dep node @@ -278,7 +269,6 @@ impl<'ast> Map<'ast> { EntryImplItem(_, item) => { let def_id = self.local_def_id(id); - assert!(!self.is_inlined_def_id(def_id)); if let Some(last_id) = last_expr { // The body of the item may have a separate dep node @@ -392,12 +382,16 @@ impl<'ast> Map<'ast> { } pub fn num_local_def_ids(&self) -> usize { - self.definitions.borrow().len() + self.definitions.len() + } + + pub fn definitions(&self) -> &Definitions { + &self.definitions } pub fn def_key(&self, def_id: DefId) -> DefKey { assert!(def_id.is_local()); - self.definitions.borrow().def_key(def_id.index) + self.definitions.def_key(def_id.index) } pub fn def_path_from_id(&self, id: NodeId) -> Option<DefPath> { @@ -408,11 +402,11 @@ impl<'ast> Map<'ast> { pub fn def_path(&self, def_id: DefId) -> DefPath { assert!(def_id.is_local()); - self.definitions.borrow().def_path(def_id.index) + self.definitions.def_path(def_id.index) } pub fn def_index_for_def_key(&self, def_key: DefKey) -> Option<DefIndex> { - self.definitions.borrow().def_index_for_def_key(def_key) + self.definitions.def_index_for_def_key(def_key) } pub fn local_def_id(&self, node: NodeId) -> DefId { @@ -423,11 +417,11 @@ impl<'ast> Map<'ast> { } pub fn opt_local_def_id(&self, node: NodeId) -> Option<DefId> { - self.definitions.borrow().opt_local_def_id(node) + self.definitions.opt_local_def_id(node) } pub fn as_local_node_id(&self, def_id: DefId) -> Option<NodeId> { - self.definitions.borrow().as_local_node_id(def_id) + self.definitions.as_local_node_id(def_id) } fn entry_count(&self) -> usize { @@ -930,23 +924,19 @@ pub fn map_crate<'ast>(forest: &'ast mut Forest, } let local_node_id_watermark = NodeId::new(map.len()); - let local_def_id_watermark = definitions.len(); Map { forest: forest, dep_graph: forest.dep_graph.clone(), map: RefCell::new(map), - definitions: RefCell::new(definitions), + definitions: definitions, local_node_id_watermark: local_node_id_watermark, - local_def_id_watermark: local_def_id_watermark, } } /// Used for items loaded from external crate that are being inlined into this /// crate. pub fn map_decoded_item<'ast>(map: &Map<'ast>, - parent_def_path: DefPath, - parent_def_id: DefId, ii: InlinedItem, ii_parent_id: NodeId) -> &'ast InlinedItem { @@ -954,18 +944,9 @@ pub fn map_decoded_item<'ast>(map: &Map<'ast>, let ii = map.forest.inlined_items.alloc(ii); - let defs = &mut *map.definitions.borrow_mut(); - let mut def_collector = DefCollector::extend(ii_parent_id, - parent_def_path.clone(), - parent_def_id, - defs); - def_collector.walk_item(ii, map.krate()); - let mut collector = NodeCollector::extend(map.krate(), ii, ii_parent_id, - parent_def_path, - parent_def_id, mem::replace(&mut *map.map.borrow_mut(), vec![])); ii.visit(&mut collector); *map.map.borrow_mut() = collector.map; diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 4fd8f96ba04..4eee76d466a 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -105,15 +105,18 @@ pub struct LifetimeDef { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)] pub struct Path { pub span: Span, - /// A `::foo` path, is relative to the crate root rather than current - /// module (like paths in an import). - pub global: bool, /// The definition that the path resolved to. pub def: Def, /// The segments in the path: the things separated by `::`. pub segments: HirVec<PathSegment>, } +impl Path { + pub fn is_global(&self) -> bool { + !self.segments.is_empty() && self.segments[0].name == keywords::CrateRoot.name() + } +} + impl fmt::Debug for Path { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "path({})", print::path_to_string(self)) @@ -475,8 +478,6 @@ pub struct MacroDef { pub attrs: HirVec<Attribute>, pub id: NodeId, pub span: Span, - pub imported_from: Option<Name>, - pub allow_internal_unstable: bool, pub body: HirVec<TokenTree>, } @@ -1533,8 +1534,6 @@ pub struct ItemId { pub id: NodeId, } -// FIXME (#3300): Should allow items to be anonymous. Right now -// we just use dummy names for anon items. /// An item /// /// The name might be a dummy name in case of anonymous items diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 74920b13280..100e344d941 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -1643,17 +1643,14 @@ impl<'a> State<'a> { -> io::Result<()> { self.maybe_print_comment(path.span.lo)?; - let mut first = !path.global; - for segment in &path.segments { - if first { - first = false - } else { + for (i, segment) in path.segments.iter().enumerate() { + if i > 0 { word(&mut self.s, "::")? } - - self.print_name(segment.name)?; - - self.print_path_parameters(&segment.parameters, colons_before_params)?; + if segment.name != keywords::CrateRoot.name() && segment.name != "$crate" { + self.print_name(segment.name)?; + self.print_path_parameters(&segment.parameters, colons_before_params)?; + } } Ok(()) @@ -1673,15 +1670,14 @@ impl<'a> State<'a> { space(&mut self.s)?; self.word_space("as")?; - let mut first = !path.global; - for segment in &path.segments[..path.segments.len() - 1] { - if first { - first = false - } else { + for (i, segment) in path.segments[..path.segments.len() - 1].iter().enumerate() { + if i > 0 { word(&mut self.s, "::")? } - self.print_name(segment.name)?; - self.print_path_parameters(&segment.parameters, colons_before_params)?; + if segment.name != keywords::CrateRoot.name() && segment.name != "$crate" { + self.print_name(segment.name)?; + self.print_path_parameters(&segment.parameters, colons_before_params)?; + } } word(&mut self.s, ">")?; diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index 90d752ae6ee..9d48fbca53e 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -1620,7 +1620,6 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> { new_segs.push(new_seg); hir::Path { span: path.span, - global: path.global, def: path.def, segments: new_segs.into() } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 9b58334e658..3e7cc0b1e3e 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1367,9 +1367,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, actual: Ty<'tcx>, - err: TypeError<'tcx>) { + err: TypeError<'tcx>) + -> DiagnosticBuilder<'tcx> { let trace = TypeTrace::types(cause, true, expected, actual); - self.report_and_explain_type_error(trace, &err).emit(); + self.report_and_explain_type_error(trace, &err) } pub fn report_conflicting_default_types(&self, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 7c26b710a53..17cc34fcd83 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -24,7 +24,6 @@ #![cfg_attr(not(stage0), deny(warnings))] #![feature(associated_consts)] -#![feature(borrow_state)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(collections)] diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 02c1ece1634..667c2590fa9 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -212,6 +212,12 @@ declare_lint! { } declare_lint! { + pub LEGACY_IMPORTS, + Warn, + "detects names that resolve to ambiguous glob imports with RFC 1560" +} + +declare_lint! { pub DEPRECATED, Warn, "detects use of deprecated items" @@ -257,6 +263,7 @@ impl LintPass for HardwiredLints { PATTERNS_IN_FNS_WITHOUT_BODY, EXTRA_REQUIREMENT_IN_IMPL, LEGACY_DIRECTORY_OWNERSHIP, + LEGACY_IMPORTS, DEPRECATED ) } diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index f2be97c8323..d0003693eef 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -25,7 +25,7 @@ use hir::def::{self, Def}; use hir::def_id::{CrateNum, DefId, DefIndex}; use hir::map as hir_map; -use hir::map::definitions::{Definitions, DefKey}; +use hir::map::definitions::{Definitions, DefKey, DisambiguatedDefPathData}; use hir::svh::Svh; use middle::lang_items; use ty::{self, Ty, TyCtxt}; @@ -298,8 +298,7 @@ pub trait CrateStore<'tcx> { // trait/impl-item info fn trait_of_item(&self, def_id: DefId) -> Option<DefId>; - fn associated_item<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> Option<ty::AssociatedItem>; + fn associated_item(&self, def: DefId) -> Option<ty::AssociatedItem>; // flags fn is_const_fn(&self, did: DefId) -> bool; @@ -336,12 +335,12 @@ pub trait CrateStore<'tcx> { fn is_no_builtins(&self, cnum: CrateNum) -> bool; // resolve - fn def_index_for_def_key(&self, - cnum: CrateNum, - def: DefKey) - -> Option<DefIndex>; - fn def_key(&self, def: DefId) -> hir_map::DefKey; - fn relative_def_path(&self, def: DefId) -> Option<hir_map::DefPath>; + fn retrace_path(&self, + cnum: CrateNum, + path_data: &[DisambiguatedDefPathData]) + -> Option<DefId>; + fn def_key(&self, def: DefId) -> DefKey; + fn def_path(&self, def: DefId) -> hir_map::DefPath; fn struct_field_names(&self, def: DefId) -> Vec<ast::Name>; fn item_children(&self, did: DefId) -> Vec<def::Export>; fn load_macro(&self, did: DefId, sess: &Session) -> LoadedMacro; @@ -442,12 +441,6 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { // trait info fn implementations_of_trait(&self, filter: Option<DefId>) -> Vec<DefId> { vec![] } - fn def_index_for_def_key(&self, - cnum: CrateNum, - def: DefKey) - -> Option<DefIndex> { - None - } // impl info fn associated_item_def_ids(&self, def_id: DefId) -> Vec<DefId> @@ -462,8 +455,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { // trait/impl-item info fn trait_of_item(&self, def_id: DefId) -> Option<DefId> { bug!("trait_of_item") } - fn associated_item<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> Option<ty::AssociatedItem> { bug!("associated_item") } + fn associated_item(&self, def: DefId) -> Option<ty::AssociatedItem> { bug!("associated_item") } // flags fn is_const_fn(&self, did: DefId) -> bool { bug!("is_const_fn") } @@ -508,8 +500,15 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn is_no_builtins(&self, cnum: CrateNum) -> bool { bug!("is_no_builtins") } // resolve - fn def_key(&self, def: DefId) -> hir_map::DefKey { bug!("def_key") } - fn relative_def_path(&self, def: DefId) -> Option<hir_map::DefPath> { + fn retrace_path(&self, + cnum: CrateNum, + path_data: &[DisambiguatedDefPathData]) + -> Option<DefId> { + None + } + + fn def_key(&self, def: DefId) -> DefKey { bug!("def_key") } + fn def_path(&self, def: DefId) -> hir_map::DefPath { bug!("relative_def_path") } fn struct_field_names(&self, def: DefId) -> Vec<ast::Name> { bug!("struct_field_names") } diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 1bf6b837fd9..1c5dd97b74b 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -86,20 +86,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } } - fn handle_definition(&mut self, id: ast::NodeId, def: Def) { - // If `bar` is a trait item, make sure to mark Foo as alive in `Foo::bar` - match def { - Def::AssociatedTy(..) | Def::Method(_) | Def::AssociatedConst(_) - if self.tcx.trait_of_item(def.def_id()).is_some() => { - if let Some(substs) = self.tcx.tables().item_substs.get(&id) { - if let ty::TyAdt(tyid, _) = substs.substs.type_at(0).sty { - self.check_def_id(tyid.did); - } - } - } - _ => {} - } - + fn handle_definition(&mut self, def: Def) { match def { Def::Const(_) | Def::AssociatedConst(..) => { self.check_def_id(def.def_id()); @@ -241,7 +228,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { match expr.node { hir::ExprPath(ref qpath @ hir::QPath::TypeRelative(..)) => { let def = self.tcx.tables().qpath_def(qpath, expr.id); - self.handle_definition(expr.id, def); + self.handle_definition(def); } hir::ExprMethodCall(..) => { self.lookup_and_handle_method(expr.id); @@ -281,7 +268,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { } PatKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { let def = self.tcx.tables().qpath_def(qpath, pat.id); - self.handle_definition(pat.id, def); + self.handle_definition(def); } _ => () } @@ -291,8 +278,8 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { self.ignore_non_const_paths = false; } - fn visit_path(&mut self, path: &'tcx hir::Path, id: ast::NodeId) { - self.handle_definition(id, path.def); + fn visit_path(&mut self, path: &'tcx hir::Path, _: ast::NodeId) { + self.handle_definition(path.def); intravisit::walk_path(self, path); } } @@ -426,6 +413,7 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> { hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) + | hir::ItemTy(..) | hir::ItemEnum(..) | hir::ItemStruct(..) | hir::ItemUnion(..) => true, diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 1efc211b8c3..029a1d66add 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -328,7 +328,6 @@ language_item_table! { PanicFmtLangItem, "panic_fmt", panic_fmt; ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn; - ExchangeFreeFnLangItem, "exchange_free", exchange_free_fn; BoxFreeFnLangItem, "box_free", box_free_fn; StrDupUniqFnLangItem, "strdup_uniq", strdup_uniq_fn; diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 3e32957aecf..f45e86f2f4b 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -302,9 +302,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { } fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) { - if md.imported_from.is_none() { - self.annotate(md.id, &md.attrs, md.span, AnnotationKind::Required, |_| {}); - } + self.annotate(md.id, &md.attrs, md.span, AnnotationKind::Required, |_| {}); } } @@ -373,9 +371,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'a, 'tcx> { } fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) { - if md.imported_from.is_none() { - self.check_missing_stability(md.id, md.span); - } + self.check_missing_stability(md.id, md.span); } } diff --git a/src/librustc/session/code_stats.rs b/src/librustc/session/code_stats.rs index 8308c54d70b..a042b2abf3a 100644 --- a/src/librustc/session/code_stats.rs +++ b/src/librustc/session/code_stats.rs @@ -142,7 +142,12 @@ impl CodeStats { max_variant_size = cmp::max(max_variant_size, size); let mut min_offset = discr_size; - for field in fields { + + // We want to print fields by increasing offset. + let mut fields = fields.clone(); + fields.sort_by_key(|f| f.offset); + + for field in fields.iter() { let FieldInfo { ref name, offset, size, align } = *field; // Include field alignment in output only if it caused padding injection diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index e500c08ce6e..ecc8042e940 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -928,6 +928,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print some statistics about MIR"), always_encode_mir: bool = (false, parse_bool, [TRACKED], "encode MIR of all functions into the crate metadata"), + osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], + "pass `-install_name @rpath/...` to the OSX linker"), } pub fn default_lib_output() -> CrateType { @@ -943,26 +945,20 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig { let vendor = &sess.target.target.target_vendor; let max_atomic_width = sess.target.target.max_atomic_width(); - let fam = if let Some(ref fam) = sess.target.target.options.target_family { - Symbol::intern(fam) - } else if sess.target.target.options.is_like_windows { - Symbol::intern("windows") - } else { - Symbol::intern("unix") - }; - let mut ret = HashSet::new(); // Target bindings. ret.insert((Symbol::intern("target_os"), Some(Symbol::intern(os)))); - ret.insert((Symbol::intern("target_family"), Some(fam))); + if let Some(ref fam) = sess.target.target.options.target_family { + ret.insert((Symbol::intern("target_family"), Some(Symbol::intern(fam)))); + if fam == "windows" || fam == "unix" { + ret.insert((Symbol::intern(fam), None)); + } + } ret.insert((Symbol::intern("target_arch"), Some(Symbol::intern(arch)))); ret.insert((Symbol::intern("target_endian"), Some(Symbol::intern(end)))); ret.insert((Symbol::intern("target_pointer_width"), Some(Symbol::intern(wordsz)))); ret.insert((Symbol::intern("target_env"), Some(Symbol::intern(env)))); ret.insert((Symbol::intern("target_vendor"), Some(Symbol::intern(vendor)))); - if fam == "windows" || fam == "unix" { - ret.insert((fam, None)); - } if sess.target.target.options.has_elf_tls { ret.insert((Symbol::intern("target_thread_local"), None)); } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 91765e68ae6..36a887e0622 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -136,6 +136,8 @@ pub struct PerfStats { pub incr_comp_bytes_hashed: Cell<u64>, // The accumulated time spent on computing symbol hashes pub symbol_hash_time: Cell<Duration>, + // The accumulated time spent decoding def path tables from metadata + pub decode_def_path_tables_time: Cell<Duration>, } impl Session { @@ -501,6 +503,8 @@ impl Session { self.perf_stats.incr_comp_hashes_count.get()); println!("Total time spent computing symbol hashes: {}", duration_to_secs_str(self.perf_stats.symbol_hash_time.get())); + println!("Total time spent decoding DefPath tables: {}", + duration_to_secs_str(self.perf_stats.decode_def_path_tables_time.get())); } } @@ -635,6 +639,7 @@ pub fn build_session_(sopts: config::Options, incr_comp_hashes_count: Cell::new(0), incr_comp_bytes_hashed: Cell::new(0), symbol_hash_time: Cell::new(Duration::from_secs(0)), + decode_def_path_tables_time: Cell::new(Duration::from_secs(0)), }, code_stats: RefCell::new(CodeStats::new()), }; diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 0c0d0c010e2..ab8c552d561 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -458,11 +458,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { err } + + /// Get the parent trait chain start + fn get_parent_trait_ref(&self, code: &ObligationCauseCode<'tcx>) -> Option<String> { + match code { + &ObligationCauseCode::BuiltinDerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_type_vars_if_possible( + &data.parent_trait_ref); + match self.get_parent_trait_ref(&data.parent_code) { + Some(t) => Some(t), + None => Some(format!("{}", parent_trait_ref.0.self_ty())), + } + } + _ => None, + } + } + pub fn report_selection_error(&self, obligation: &PredicateObligation<'tcx>, error: &SelectionError<'tcx>) { let span = obligation.cause.span; + let mut err = match *error { SelectionError::Unimplemented => { if let ObligationCauseCode::CompareImplMethodObligation { @@ -487,14 +504,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { return; } else { let trait_ref = trait_predicate.to_poly_trait_ref(); - - let mut err = struct_span_err!(self.tcx.sess, span, E0277, - "the trait bound `{}` is not satisfied", - trait_ref.to_predicate()); - err.span_label(span, &format!("the trait `{}` is not implemented \ - for `{}`", - trait_ref, - trait_ref.self_ty())); + let (post_message, pre_message) = match self.get_parent_trait_ref( + &obligation.cause.code) + { + Some(t) => { + (format!(" in `{}`", t), format!("within `{}`, ", t)) + } + None => (String::new(), String::new()), + }; + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0277, + "the trait bound `{}` is not satisfied{}", + trait_ref.to_predicate(), + post_message); + err.span_label(span, + &format!("{}the trait `{}` is not \ + implemented for `{}`", + pre_message, + trait_ref, + trait_ref.self_ty())); // Try to report a help message diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 4854a14f733..f24ff980355 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -15,9 +15,9 @@ use session::Session; use middle; use hir::TraitMap; use hir::def::Def; -use hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE}; +use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use hir::map as ast_map; -use hir::map::{DefKey, DefPathData, DisambiguatedDefPathData}; +use hir::map::DisambiguatedDefPathData; use middle::free_region::FreeRegionMap; use middle::region::RegionMaps; use middle::resolve_lifetime; @@ -627,50 +627,21 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - /// Given a def-key `key` and a crate `krate`, finds the def-index - /// that `krate` assigned to `key`. This `DefIndex` will always be - /// relative to `krate`. - /// - /// Returns `None` if there is no `DefIndex` with that key. - pub fn def_index_for_def_key(self, krate: CrateNum, key: DefKey) - -> Option<DefIndex> { - if krate == LOCAL_CRATE { - self.map.def_index_for_def_key(key) - } else { - self.sess.cstore.def_index_for_def_key(krate, key) - } - } - pub fn retrace_path(self, krate: CrateNum, path_data: &[DisambiguatedDefPathData]) -> Option<DefId> { debug!("retrace_path(path={:?}, krate={:?})", path_data, self.crate_name(krate)); - let root_key = DefKey { - parent: None, - disambiguated_data: DisambiguatedDefPathData { - data: DefPathData::CrateRoot, - disambiguator: 0, - }, - }; - - let root_index = self.def_index_for_def_key(krate, root_key) - .expect("no root key?"); - - debug!("retrace_path: root_index={:?}", root_index); - - let mut index = root_index; - for data in path_data { - let key = DefKey { parent: Some(index), disambiguated_data: data.clone() }; - debug!("retrace_path: key={:?}", key); - match self.def_index_for_def_key(krate, key) { - Some(i) => index = i, - None => return None, - } + if krate == LOCAL_CRATE { + self.map + .definitions() + .def_path_table() + .retrace_path(path_data) + .map(|def_index| DefId { krate: krate, index: def_index }) + } else { + self.sess.cstore.retrace_path(krate, path_data) } - - Some(DefId { krate: krate, index: index }) } pub fn type_parameter_def(self, @@ -762,11 +733,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// reference to the context, to allow formatting values that need it. pub fn create_and_enter<F, R>(s: &'tcx Session, arenas: &'tcx CtxtArenas<'tcx>, - trait_map: TraitMap, + resolutions: ty::Resolutions, named_region_map: resolve_lifetime::NamedRegionMap, map: ast_map::Map<'tcx>, - freevars: FreevarMap, - maybe_unused_trait_imports: NodeSet, region_maps: RegionMaps, lang_items: middle::lang_items::LanguageItems, stability: stability::Index<'tcx>, @@ -790,7 +759,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { item_variance_map: RefCell::new(DepTrackingMap::new(dep_graph.clone())), variance_computed: Cell::new(false), sess: s, - trait_map: trait_map, + trait_map: resolutions.trait_map, tables: RefCell::new(Tables::empty()), impl_trait_refs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), trait_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), @@ -802,8 +771,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { fulfilled_predicates: RefCell::new(fulfilled_predicates), map: map, mir_map: RefCell::new(DepTrackingMap::new(dep_graph.clone())), - freevars: RefCell::new(freevars), - maybe_unused_trait_imports: maybe_unused_trait_imports, + freevars: RefCell::new(resolutions.freevars), + maybe_unused_trait_imports: resolutions.maybe_unused_trait_imports, item_types: RefCell::new(DepTrackingMap::new(dep_graph.clone())), rcache: RefCell::new(FxHashMap()), tc_cache: RefCell::new(FxHashMap()), diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index 440a3916786..0e4c14029e9 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -160,11 +160,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.push_krate_path(buffer, def_id.krate); } - DefPathData::InlinedRoot(ref root_path) => { - assert!(key.parent.is_none()); - self.push_item_path(buffer, root_path.def_id); - } - DefPathData::Impl => { self.push_impl_path(buffer, def_id); } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 8646bccf1e9..ebac30c8e50 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -24,6 +24,7 @@ use syntax_pos::DUMMY_SP; use std::cmp; use std::fmt; use std::i64; +use std::iter; /// Parsed [Data layout](http://llvm.org/docs/LangRef.html#data-layout) /// for a target, which contains everything needed to compute layouts. @@ -415,7 +416,7 @@ impl Integer { /// signed discriminant range and #[repr] attribute. /// N.B.: u64 values above i64::MAX will be treated as signed, but /// that shouldn't affect anything, other than maybe debuginfo. - pub fn repr_discr(tcx: TyCtxt, ty: Ty, hint: attr::ReprAttr, min: i64, max: i64) + fn repr_discr(tcx: TyCtxt, ty: Ty, hints: &[attr::ReprAttr], min: i64, max: i64) -> (Integer, bool) { // Theoretically, negative values could be larger in unsigned representation // than the unsigned representation of the signed minimum. However, if there @@ -424,34 +425,41 @@ impl Integer { let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u64, max as u64)); let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); - let at_least = match hint { - attr::ReprInt(ity) => { - let discr = Integer::from_attr(&tcx.data_layout, ity); - let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; - if discr < fit { - bug!("Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum `{}", ty) + let mut min_from_extern = None; + let min_default = I8; + + for &r in hints.iter() { + match r { + attr::ReprInt(ity) => { + let discr = Integer::from_attr(&tcx.data_layout, ity); + let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; + if discr < fit { + bug!("Integer::repr_discr: `#[repr]` hint too small for \ + discriminant range of enum `{}", ty) + } + return (discr, ity.is_signed()); } - return (discr, ity.is_signed()); - } - attr::ReprExtern => { - match &tcx.sess.target.target.arch[..] { - // WARNING: the ARM EABI has two variants; the one corresponding - // to `at_least == I32` appears to be used on Linux and NetBSD, - // but some systems may use the variant corresponding to no - // lower bound. However, we don't run on those yet...? - "arm" => I32, - _ => I32, + attr::ReprExtern => { + match &tcx.sess.target.target.arch[..] { + // WARNING: the ARM EABI has two variants; the one corresponding + // to `at_least == I32` appears to be used on Linux and NetBSD, + // but some systems may use the variant corresponding to no + // lower bound. However, we don't run on those yet...? + "arm" => min_from_extern = Some(I32), + _ => min_from_extern = Some(I32), + } + } + attr::ReprAny => {}, + attr::ReprPacked => { + bug!("Integer::repr_discr: found #[repr(packed)] on enum `{}", ty); + } + attr::ReprSimd => { + bug!("Integer::repr_discr: found #[repr(simd)] on enum `{}", ty); } } - attr::ReprAny => I8, - attr::ReprPacked => { - bug!("Integer::repr_discr: found #[repr(packed)] on enum `{}", ty); - } - attr::ReprSimd => { - bug!("Integer::repr_discr: found #[repr(simd)] on enum `{}", ty); - } - }; + } + + let at_least = min_from_extern.unwrap_or(min_default); // If there are no negative values, we can use the unsigned fit. if min >= 0 { @@ -511,70 +519,162 @@ pub struct Struct { /// If true, the size is exact, otherwise it's only a lower bound. pub sized: bool, - /// Offsets for the first byte of each field. + /// Offsets for the first byte of each field, ordered to match the source definition order. + /// This vector does not go in increasing order. /// FIXME(eddyb) use small vector optimization for the common case. pub offsets: Vec<Size>, + /// Maps source order field indices to memory order indices, depending how fields were permuted. + /// FIXME (camlorn) also consider small vector optimization here. + pub memory_index: Vec<u32>, + pub min_size: Size, } +// Info required to optimize struct layout. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +enum StructKind { + // A tuple, closure, or univariant which cannot be coerced to unsized. + AlwaysSizedUnivariant, + // A univariant, the last field of which may be coerced to unsized. + MaybeUnsizedUnivariant, + // A univariant, but part of an enum. + EnumVariant, +} + impl<'a, 'gcx, 'tcx> Struct { - pub fn new(dl: &TargetDataLayout, packed: bool) -> Struct { - Struct { + // FIXME(camlorn): reprs need a better representation to deal with multiple reprs on one type. + fn new(dl: &TargetDataLayout, fields: &Vec<&'a Layout>, + reprs: &[attr::ReprAttr], kind: StructKind, + scapegoat: Ty<'gcx>) -> Result<Struct, LayoutError<'gcx>> { + let packed = reprs.contains(&attr::ReprPacked); + let mut ret = Struct { align: if packed { dl.i8_align } else { dl.aggregate_align }, packed: packed, sized: true, offsets: vec![], + memory_index: vec![], min_size: Size::from_bytes(0), + }; + + // Anything with ReprExtern or ReprPacked doesn't optimize. + // Neither do 1-member and 2-member structs. + // In addition, code in trans assume that 2-element structs can become pairs. + // It's easier to just short-circuit here. + let mut can_optimize = fields.len() > 2 || StructKind::EnumVariant == kind; + if can_optimize { + // This exhaustive match makes new reprs force the adder to modify this function. + // Otherwise, things can silently break. + // Note the inversion, return true to stop optimizing. + can_optimize = !reprs.iter().any(|r| { + match *r { + attr::ReprAny | attr::ReprInt(_) => false, + attr::ReprExtern | attr::ReprPacked => true, + attr::ReprSimd => bug!("Simd vectors should be represented as layout::Vector") + } + }); } - } - /// Extend the Struct with more fields. - pub fn extend<I>(&mut self, dl: &TargetDataLayout, - fields: I, - scapegoat: Ty<'gcx>) - -> Result<(), LayoutError<'gcx>> - where I: Iterator<Item=Result<&'a Layout, LayoutError<'gcx>>> { - self.offsets.reserve(fields.size_hint().0); + // Disable field reordering until we can decide what to do. + // The odd pattern here avoids a warning about the value never being read. + if can_optimize { can_optimize = false } - let mut offset = self.min_size; + let (optimize, sort_ascending) = match kind { + StructKind::AlwaysSizedUnivariant => (can_optimize, false), + StructKind::MaybeUnsizedUnivariant => (can_optimize, false), + StructKind::EnumVariant => { + assert!(fields.len() >= 1, "Enum variants must have discriminants."); + (can_optimize && fields[0].size(dl).bytes() == 1, true) + } + }; - for field in fields { - if !self.sized { - bug!("Struct::extend: field #{} of `{}` comes after unsized field", - self.offsets.len(), scapegoat); + ret.offsets = vec![Size::from_bytes(0); fields.len()]; + let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect(); + + if optimize { + let start = if let StructKind::EnumVariant = kind { 1 } else { 0 }; + let end = if let StructKind::MaybeUnsizedUnivariant = kind { + fields.len() - 1 + } else { + fields.len() + }; + if end > start { + let optimizing = &mut inverse_memory_index[start..end]; + if sort_ascending { + optimizing.sort_by_key(|&x| fields[x as usize].align(dl).abi()); + } else { + optimizing.sort_by(| &a, &b | { + let a = fields[a as usize].align(dl).abi(); + let b = fields[b as usize].align(dl).abi(); + b.cmp(&a) + }); + } + } + } + + // inverse_memory_index holds field indices by increasing memory offset. + // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5. + // We now write field offsets to the corresponding offset slot; + // field 5 with offset 0 puts 0 in offsets[5]. + // At the bottom of this function, we use inverse_memory_index to produce memory_index. + + if let StructKind::EnumVariant = kind { + assert_eq!(inverse_memory_index[0], 0, + "Enum variant discriminants must have the lowest offset."); + } + + let mut offset = Size::from_bytes(0); + + for i in inverse_memory_index.iter() { + let field = fields[*i as usize]; + if !ret.sized { + bug!("Struct::new: field #{} of `{}` comes after unsized field", + ret.offsets.len(), scapegoat); } - let field = field?; if field.is_unsized() { - self.sized = false; + ret.sized = false; } // Invariant: offset < dl.obj_size_bound() <= 1<<61 - if !self.packed { + if !ret.packed { let align = field.align(dl); - self.align = self.align.max(align); + ret.align = ret.align.max(align); offset = offset.abi_align(align); } - self.offsets.push(offset); - - debug!("Struct::extend offset: {:?} field: {:?} {:?}", offset, field, field.size(dl)); + debug!("Struct::new offset: {:?} field: {:?} {:?}", offset, field, field.size(dl)); + ret.offsets[*i as usize] = offset; offset = offset.checked_add(field.size(dl), dl) .map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?; } - debug!("Struct::extend min_size: {:?}", offset); - self.min_size = offset; + debug!("Struct::new min_size: {:?}", offset); + ret.min_size = offset; - Ok(()) - } + // As stated above, inverse_memory_index holds field indices by increasing offset. + // This makes it an already-sorted view of the offsets vec. + // To invert it, consider: + // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. + // Field 5 would be the first element, so memory_index is i: + // Note: if we didn't optimize, it's already right. - /// Get the size without trailing alignment padding. + if optimize { + ret.memory_index = vec![0; inverse_memory_index.len()]; - /// Get the size with trailing aligment padding. + for i in 0..inverse_memory_index.len() { + ret.memory_index[inverse_memory_index[i] as usize] = i as u32; + } + } else { + ret.memory_index = inverse_memory_index; + } + + Ok(ret) + } + + /// Get the size with trailing alignment padding. pub fn stride(&self) -> Size { self.min_size.abi_align(self.align) } @@ -592,18 +692,45 @@ impl<'a, 'gcx, 'tcx> Struct { Ok(true) } + /// Get indices of the tys that made this struct by increasing offset. + #[inline] + pub fn field_index_by_increasing_offset<'b>(&'b self) -> impl iter::Iterator<Item=usize>+'b { + let mut inverse_small = [0u8; 64]; + let mut inverse_big = vec![]; + let use_small = self.memory_index.len() <= inverse_small.len(); + + // We have to write this logic twice in order to keep the array small. + if use_small { + for i in 0..self.memory_index.len() { + inverse_small[self.memory_index[i] as usize] = i as u8; + } + } else { + inverse_big = vec![0; self.memory_index.len()]; + for i in 0..self.memory_index.len() { + inverse_big[self.memory_index[i] as usize] = i as u32; + } + } + + (0..self.memory_index.len()).map(move |i| { + if use_small { inverse_small[i] as usize } + else { inverse_big[i] as usize } + }) + } + /// Find the path leading to a non-zero leaf field, starting from /// the given type and recursing through aggregates. + /// The tuple is `(path, source_path)`, + /// where `path` is in memory order and `source_path` in source order. // FIXME(eddyb) track value ranges and traverse already optimized enums. - pub fn non_zero_field_in_type(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - ty: Ty<'gcx>) - -> Result<Option<FieldPath>, LayoutError<'gcx>> { + fn non_zero_field_in_type(infcx: &InferCtxt<'a, 'gcx, 'tcx>, + ty: Ty<'gcx>) + -> Result<Option<(FieldPath, FieldPath)>, LayoutError<'gcx>> { let tcx = infcx.tcx.global_tcx(); match (ty.layout(infcx)?, &ty.sty) { (&Scalar { non_zero: true, .. }, _) | - (&CEnum { non_zero: true, .. }, _) => Ok(Some(vec![])), + (&CEnum { non_zero: true, .. }, _) => Ok(Some((vec![], vec![]))), (&FatPointer { non_zero: true, .. }, _) => { - Ok(Some(vec![FAT_PTR_ADDR as u32])) + Ok(Some((vec![FAT_PTR_ADDR as u32], vec![FAT_PTR_ADDR as u32]))) } // Is this the NonZero lang item wrapping a pointer or integer type? @@ -614,10 +741,11 @@ impl<'a, 'gcx, 'tcx> Struct { // FIXME(eddyb) also allow floating-point types here. Scalar { value: Int(_), non_zero: false } | Scalar { value: Pointer, non_zero: false } => { - Ok(Some(vec![0])) + Ok(Some((vec![0], vec![0]))) } FatPointer { non_zero: false, .. } => { - Ok(Some(vec![FAT_PTR_ADDR as u32, 0])) + let tmp = vec![FAT_PTR_ADDR as u32, 0]; + Ok(Some((tmp.clone(), tmp))) } _ => Ok(None) } @@ -625,27 +753,30 @@ impl<'a, 'gcx, 'tcx> Struct { // Perhaps one of the fields of this struct is non-zero // let's recurse and find out - (_, &ty::TyAdt(def, substs)) if def.is_struct() => { - Struct::non_zero_field_path(infcx, def.struct_variant().fields + (&Univariant { ref variant, .. }, &ty::TyAdt(def, substs)) if def.is_struct() => { + Struct::non_zero_field_paths(infcx, def.struct_variant().fields .iter().map(|field| { field.ty(tcx, substs) - })) + }), + Some(&variant.memory_index[..])) } // Perhaps one of the upvars of this closure is non-zero - // Let's recurse and find out! - (_, &ty::TyClosure(def_id, ref substs)) => { - Struct::non_zero_field_path(infcx, substs.upvar_tys(def_id, tcx)) + (&Univariant { ref variant, .. }, &ty::TyClosure(def, substs)) => { + let upvar_tys = substs.upvar_tys(def, tcx); + Struct::non_zero_field_paths(infcx, upvar_tys, + Some(&variant.memory_index[..])) } // Can we use one of the fields in this tuple? - (_, &ty::TyTuple(tys)) => { - Struct::non_zero_field_path(infcx, tys.iter().cloned()) + (&Univariant { ref variant, .. }, &ty::TyTuple(tys)) => { + Struct::non_zero_field_paths(infcx, tys.iter().cloned(), + Some(&variant.memory_index[..])) } // Is this a fixed-size array of something non-zero // with at least one element? (_, &ty::TyArray(ety, d)) if d > 0 => { - Struct::non_zero_field_path(infcx, Some(ety).into_iter()) + Struct::non_zero_field_paths(infcx, Some(ety).into_iter(), None) } (_, &ty::TyProjection(_)) | (_, &ty::TyAnon(..)) => { @@ -663,14 +794,23 @@ impl<'a, 'gcx, 'tcx> Struct { /// Find the path leading to a non-zero leaf field, starting from /// the given set of fields and recursing through aggregates. - pub fn non_zero_field_path<I>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - fields: I) - -> Result<Option<FieldPath>, LayoutError<'gcx>> + /// Returns Some((path, source_path)) on success. + /// `path` is translated to memory order. `source_path` is not. + fn non_zero_field_paths<I>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, + fields: I, + permutation: Option<&[u32]>) + -> Result<Option<(FieldPath, FieldPath)>, LayoutError<'gcx>> where I: Iterator<Item=Ty<'gcx>> { for (i, ty) in fields.enumerate() { - if let Some(mut path) = Struct::non_zero_field_in_type(infcx, ty)? { - path.push(i as u32); - return Ok(Some(path)); + if let Some((mut path, mut source_path)) = Struct::non_zero_field_in_type(infcx, ty)? { + source_path.push(i as u32); + let index = if let Some(p) = permutation { + p[i] as usize + } else { + i + }; + path.push(index as u32); + return Ok(Some((path, source_path))); } } Ok(None) @@ -723,7 +863,7 @@ impl<'a, 'gcx, 'tcx> Union { Ok(()) } - /// Get the size with trailing aligment padding. + /// Get the size with trailing alignment padding. pub fn stride(&self) -> Size { self.min_size.abi_align(self.align) } @@ -833,7 +973,9 @@ pub enum Layout { nndiscr: u64, nonnull: Struct, // N.B. There is a 0 at the start, for LLVM GEP through a pointer. - discrfield: FieldPath + discrfield: FieldPath, + // Like discrfield, but in source order. For debuginfo. + discrfield_source: FieldPath } } @@ -887,6 +1029,7 @@ impl<'a, 'gcx, 'tcx> Layout { let dl = &tcx.data_layout; assert!(!ty.has_infer_types()); + let layout = match ty.sty { // Basic scalars. ty::TyBool => Scalar { value: Int(I1), non_zero: false }, @@ -908,7 +1051,11 @@ impl<'a, 'gcx, 'tcx> Layout { ty::TyFnPtr(_) => Scalar { value: Pointer, non_zero: true }, // The never type. - ty::TyNever => Univariant { variant: Struct::new(dl, false), non_zero: false }, + ty::TyNever => Univariant { + variant: Struct::new(dl, &vec![], &[], + StructKind::AlwaysSizedUnivariant, ty)?, + non_zero: false + }, // Potentially-fat pointers. ty::TyBox(pointee) | @@ -959,27 +1106,36 @@ impl<'a, 'gcx, 'tcx> Layout { // Odd unit types. ty::TyFnDef(..) => { Univariant { - variant: Struct::new(dl, false), + variant: Struct::new(dl, &vec![], + &[], StructKind::AlwaysSizedUnivariant, ty)?, non_zero: false } } ty::TyDynamic(..) => { - let mut unit = Struct::new(dl, false); + let mut unit = Struct::new(dl, &vec![], &[], + StructKind::AlwaysSizedUnivariant, ty)?; unit.sized = false; Univariant { variant: unit, non_zero: false } } // Tuples and closures. ty::TyClosure(def_id, ref substs) => { - let mut st = Struct::new(dl, false); let tys = substs.upvar_tys(def_id, tcx); - st.extend(dl, tys.map(|ty| ty.layout(infcx)), ty)?; + let st = Struct::new(dl, + &tys.map(|ty| ty.layout(infcx)) + .collect::<Result<Vec<_>, _>>()?, + &[], + StructKind::AlwaysSizedUnivariant, ty)?; Univariant { variant: st, non_zero: false } } ty::TyTuple(tys) => { - let mut st = Struct::new(dl, false); - st.extend(dl, tys.iter().map(|ty| ty.layout(infcx)), ty)?; + // FIXME(camlorn): if we ever allow unsized tuples, this needs to be checked. + // See the univariant case below to learn how. + let st = Struct::new(dl, + &tys.iter().map(|ty| ty.layout(infcx)) + .collect::<Result<Vec<_>, _>>()?, + &[], StructKind::AlwaysSizedUnivariant, ty)?; Univariant { variant: st, non_zero: false } } @@ -1003,16 +1159,16 @@ impl<'a, 'gcx, 'tcx> Layout { // ADTs. ty::TyAdt(def, substs) => { - let hint = *tcx.lookup_repr_hints(def.did).get(0) - .unwrap_or(&attr::ReprAny); + let hints = &tcx.lookup_repr_hints(def.did)[..]; if def.variants.is_empty() { // Uninhabitable; represent as unit // (Typechecking will reject discriminant-sizing attrs.) - assert_eq!(hint, attr::ReprAny); + assert_eq!(hints.len(), 0); return success(Univariant { - variant: Struct::new(dl, false), + variant: Struct::new(dl, &vec![], + &hints[..], StructKind::AlwaysSizedUnivariant, ty)?, non_zero: false }); } @@ -1027,7 +1183,7 @@ impl<'a, 'gcx, 'tcx> Layout { if x > max { max = x; } } - let (discr, signed) = Integer::repr_discr(tcx, ty, hint, min, max); + let (discr, signed) = Integer::repr_discr(tcx, ty, &hints[..], min, max); return success(CEnum { discr: discr, signed: signed, @@ -1037,21 +1193,35 @@ impl<'a, 'gcx, 'tcx> Layout { }); } - if !def.is_enum() || def.variants.len() == 1 && hint == attr::ReprAny { + if !def.is_enum() || def.variants.len() == 1 && hints.is_empty() { // Struct, or union, or univariant enum equivalent to a struct. // (Typechecking will reject discriminant-sizing attrs.) + let kind = if def.is_enum() || def.variants[0].fields.len() == 0{ + StructKind::AlwaysSizedUnivariant + } else { + use middle::region::ROOT_CODE_EXTENT; + let param_env = tcx.construct_parameter_environment(DUMMY_SP, + def.did, ROOT_CODE_EXTENT); + let fields = &def.variants[0].fields; + let last_field = &fields[fields.len()-1]; + let always_sized = last_field.ty(tcx, param_env.free_substs) + .is_sized(tcx, ¶m_env, DUMMY_SP); + if !always_sized { StructKind::MaybeUnsizedUnivariant } + else { StructKind::AlwaysSizedUnivariant } + }; + let fields = def.variants[0].fields.iter().map(|field| { field.ty(tcx, substs).layout(infcx) - }); + }).collect::<Result<Vec<_>, _>>()?; let packed = tcx.lookup_packed(def.did); let layout = if def.is_union() { let mut un = Union::new(dl, packed); - un.extend(dl, fields, ty)?; + un.extend(dl, fields.iter().map(|&f| Ok(f)), ty)?; UntaggedUnion { variants: un } } else { - let mut st = Struct::new(dl, packed); - st.extend(dl, fields, ty)?; + let st = Struct::new(dl, &fields, &hints[..], + kind, ty)?; let non_zero = Some(def.did) == tcx.lang_items.non_zero(); Univariant { variant: st, non_zero: non_zero } }; @@ -1073,7 +1243,7 @@ impl<'a, 'gcx, 'tcx> Layout { v.fields.iter().map(|field| field.ty(tcx, substs)).collect::<Vec<_>>() }).collect::<Vec<_>>(); - if variants.len() == 2 && hint == attr::ReprAny { + if variants.len() == 2 && hints.is_empty() { // Nullable pointer optimization for discr in 0..2 { let other_fields = variants[1 - discr].iter().map(|ty| { @@ -1082,9 +1252,11 @@ impl<'a, 'gcx, 'tcx> Layout { if !Struct::would_be_zero_sized(dl, other_fields)? { continue; } - let path = Struct::non_zero_field_path(infcx, - variants[discr].iter().cloned())?; - let mut path = if let Some(p) = path { p } else { continue }; + let paths = Struct::non_zero_field_paths(infcx, + variants[discr].iter().cloned(), + None)?; + let (mut path, mut path_source) = if let Some(p) = paths { p } + else { continue }; // FIXME(eddyb) should take advantage of a newtype. if path == &[0] && variants[discr].len() == 1 { @@ -1101,14 +1273,25 @@ impl<'a, 'gcx, 'tcx> Layout { }); } + let st = Struct::new(dl, + &variants[discr].iter().map(|ty| ty.layout(infcx)) + .collect::<Result<Vec<_>, _>>()?, + &hints[..], StructKind::AlwaysSizedUnivariant, ty)?; + + // We have to fix the last element of path here. + let mut i = *path.last().unwrap(); + i = st.memory_index[i as usize]; + *path.last_mut().unwrap() = i; path.push(0); // For GEP through a pointer. path.reverse(); - let mut st = Struct::new(dl, false); - st.extend(dl, variants[discr].iter().map(|ty| ty.layout(infcx)), ty)?; + path_source.push(0); + path_source.reverse(); + return success(StructWrappedNullablePointer { nndiscr: discr as u64, nonnull: st, - discrfield: path + discrfield: path, + discrfield_source: path_source }); } } @@ -1116,7 +1299,7 @@ impl<'a, 'gcx, 'tcx> Layout { // The general case. let discr_max = (variants.len() - 1) as i64; assert!(discr_max >= 0); - let (min_ity, _) = Integer::repr_discr(tcx, ty, hint, 0, discr_max); + let (min_ity, _) = Integer::repr_discr(tcx, ty, &hints[..], 0, discr_max); let mut align = dl.aggregate_align; let mut size = Size::from_bytes(0); @@ -1126,24 +1309,26 @@ impl<'a, 'gcx, 'tcx> Layout { // Create the set of structs that represent each variant // Use the minimum integer type we figured out above - let discr = Some(Scalar { value: Int(min_ity), non_zero: false }); + let discr = Scalar { value: Int(min_ity), non_zero: false }; let mut variants = variants.into_iter().map(|fields| { - let mut found_start = false; - let fields = fields.into_iter().map(|field| { - let field = field.layout(infcx)?; - if !found_start { - // Find the first field we can't move later - // to make room for a larger discriminant. - let field_align = field.align(dl); - if field.size(dl).bytes() != 0 || field_align.abi() != 1 { - start_align = start_align.min(field_align); - found_start = true; - } + let mut fields = fields.into_iter().map(|field| { + field.layout(infcx) + }).collect::<Result<Vec<_>, _>>()?; + fields.insert(0, &discr); + let st = Struct::new(dl, + &fields, + &hints[..], StructKind::EnumVariant, ty)?; + // Find the first field we can't move later + // to make room for a larger discriminant. + // It is important to skip the first field. + for i in st.field_index_by_increasing_offset().skip(1) { + let field = fields[i]; + let field_align = field.align(dl); + if field.size(dl).bytes() != 0 || field_align.abi() != 1 { + start_align = start_align.min(field_align); + break; } - Ok(field) - }); - let mut st = Struct::new(dl, false); - st.extend(dl, discr.iter().map(Ok).chain(fields), ty)?; + } size = cmp::max(size, st.min_size); align = align.max(st.align); Ok(st) @@ -1177,11 +1362,12 @@ impl<'a, 'gcx, 'tcx> Layout { let old_ity_size = Int(min_ity).size(dl); let new_ity_size = Int(ity).size(dl); for variant in &mut variants { - for offset in &mut variant.offsets[1..] { - if *offset > old_ity_size { - break; + for i in variant.offsets.iter_mut() { + // The first field is the discrimminant, at offset 0. + // These aren't in order, and we need to skip it. + if *i <= old_ity_size && *i > Size::from_bytes(0) { + *i = new_ity_size; } - *offset = new_ity_size; } // We might be making the struct larger. if variant.min_size <= old_ity_size { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index df12c252907..2ab10d0446b 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -17,7 +17,7 @@ pub use self::LvaluePreference::*; pub use self::fold::TypeFoldable; use dep_graph::{self, DepNode}; -use hir::map as ast_map; +use hir::{map as ast_map, FreevarMap, TraitMap}; use middle; use hir::def::{Def, CtorKind, ExportMap}; use hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; @@ -112,6 +112,13 @@ pub struct CrateAnalysis<'tcx> { pub hir_ty_to_ty: NodeMap<Ty<'tcx>>, } +#[derive(Clone)] +pub struct Resolutions { + pub freevars: FreevarMap, + pub trait_map: TraitMap, + pub maybe_unused_trait_imports: NodeSet, +} + #[derive(Copy, Clone)] pub enum DtorKind { NoDtor, @@ -212,26 +219,18 @@ pub enum Visibility { /// Visible everywhere (including in other crates). Public, /// Visible only in the given crate-local module. - Restricted(NodeId), + Restricted(DefId), /// Not visible anywhere in the local crate. This is the visibility of private external items. - PrivateExternal, + Invisible, } -pub trait NodeIdTree { - fn is_descendant_of(&self, node: NodeId, ancestor: NodeId) -> bool; +pub trait DefIdTree: Copy { + fn parent(self, id: DefId) -> Option<DefId>; } -impl<'a> NodeIdTree for ast_map::Map<'a> { - fn is_descendant_of(&self, node: NodeId, ancestor: NodeId) -> bool { - let mut node_ancestor = node; - while node_ancestor != ancestor { - let node_ancestor_parent = self.get_module_parent(node_ancestor); - if node_ancestor_parent == node_ancestor { - return false; - } - node_ancestor = node_ancestor_parent; - } - true +impl<'a, 'gcx, 'tcx> DefIdTree for TyCtxt<'a, 'gcx, 'tcx> { + fn parent(self, id: DefId) -> Option<DefId> { + self.def_key(id).parent.map(|index| DefId { index: index, ..id }) } } @@ -239,36 +238,46 @@ impl Visibility { pub fn from_hir(visibility: &hir::Visibility, id: NodeId, tcx: TyCtxt) -> Self { match *visibility { hir::Public => Visibility::Public, - hir::Visibility::Crate => Visibility::Restricted(ast::CRATE_NODE_ID), + hir::Visibility::Crate => Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)), hir::Visibility::Restricted { ref path, .. } => match path.def { // If there is no resolution, `resolve` will have already reported an error, so // assume that the visibility is public to avoid reporting more privacy errors. Def::Err => Visibility::Public, - def => Visibility::Restricted(tcx.map.as_local_node_id(def.def_id()).unwrap()), + def => Visibility::Restricted(def.def_id()), }, - hir::Inherited => Visibility::Restricted(tcx.map.get_module_parent(id)), + hir::Inherited => { + Visibility::Restricted(tcx.map.local_def_id(tcx.map.get_module_parent(id))) + } } } /// Returns true if an item with this visibility is accessible from the given block. - pub fn is_accessible_from<T: NodeIdTree>(self, block: NodeId, tree: &T) -> bool { + pub fn is_accessible_from<T: DefIdTree>(self, mut module: DefId, tree: T) -> bool { let restriction = match self { // Public items are visible everywhere. Visibility::Public => return true, // Private items from other crates are visible nowhere. - Visibility::PrivateExternal => return false, + Visibility::Invisible => return false, // Restricted items are visible in an arbitrary local module. + Visibility::Restricted(other) if other.krate != module.krate => return false, Visibility::Restricted(module) => module, }; - tree.is_descendant_of(block, restriction) + while module != restriction { + match tree.parent(module) { + Some(parent) => module = parent, + None => return false, + } + } + + true } /// Returns true if this visibility is at least as accessible as the given visibility - pub fn is_at_least<T: NodeIdTree>(self, vis: Visibility, tree: &T) -> bool { + pub fn is_at_least<T: DefIdTree>(self, vis: Visibility, tree: T) -> bool { let vis_restriction = match vis { Visibility::Public => return self == Visibility::Public, - Visibility::PrivateExternal => return true, + Visibility::Invisible => return true, Visibility::Restricted(module) => module, }; @@ -1772,7 +1781,7 @@ impl<'a, 'gcx, 'tcx> FieldDef { block: Option<NodeId>, tcx: TyCtxt<'a, 'gcx, 'tcx>, substs: &'tcx Substs<'tcx>) -> bool { - block.map_or(true, |b| self.vis.is_accessible_from(b, &tcx.map)) && + block.map_or(true, |b| tcx.vis_is_accessible_from(self.vis, b)) && self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx) } } @@ -2062,7 +2071,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn associated_item(self, def_id: DefId) -> AssociatedItem { self.associated_items.memoize(def_id, || { if !def_id.is_local() { - return self.sess.cstore.associated_item(self.global_tcx(), def_id) + return self.sess.cstore.associated_item(def_id) .expect("missing AssociatedItem in metadata"); } @@ -2241,40 +2250,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Convert a `DefId` into its fully expanded `DefPath` (every /// `DefId` is really just an interned def-path). /// - /// Note that if `id` is not local to this crate -- or is - /// inlined into this crate -- the result will be a non-local - /// `DefPath`. - /// - /// This function is only safe to use when you are sure that the - /// full def-path is accessible. Examples that are known to be - /// safe are local def-ids or items; see `opt_def_path` for more - /// details. + /// Note that if `id` is not local to this crate, the result will + // be a non-local `DefPath`. pub fn def_path(self, id: DefId) -> ast_map::DefPath { - self.opt_def_path(id).unwrap_or_else(|| { - bug!("could not load def-path for {:?}", id) - }) - } - - /// Convert a `DefId` into its fully expanded `DefPath` (every - /// `DefId` is really just an interned def-path). - /// - /// When going across crates, we do not save the full info for - /// every cross-crate def-id, and hence we may not always be able - /// to create a def-path. Therefore, this returns - /// `Option<DefPath>` to cover that possibility. It will always - /// return `Some` for local def-ids, however, as well as for - /// items. The problems arise with "minor" def-ids like those - /// associated with a pattern, `impl Trait`, or other internal - /// detail to a fn. - /// - /// Note that if `id` is not local to this crate -- or is - /// inlined into this crate -- the result will be a non-local - /// `DefPath`. - pub fn opt_def_path(self, id: DefId) -> Option<ast_map::DefPath> { if id.is_local() { - Some(self.map.def_path(id)) + self.map.def_path(id) } else { - self.sess.cstore.relative_def_path(id) + self.sess.cstore.def_path(id) } } @@ -2286,6 +2268,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + pub fn vis_is_accessible_from(self, vis: Visibility, block: NodeId) -> bool { + vis.is_accessible_from(self.map.local_def_id(self.map.get_module_parent(block)), self) + } + pub fn item_name(self, id: DefId) -> ast::Name { if let Some(id) = self.map.as_local_node_id(id) { self.map.name(id) @@ -2540,8 +2526,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// ID of the impl that the method belongs to. Otherwise, return `None`. pub fn impl_of_method(self, def_id: DefId) -> Option<DefId> { if def_id.krate != LOCAL_CRATE { - return self.sess.cstore.associated_item(self.global_tcx(), def_id) - .and_then(|item| { + return self.sess.cstore.associated_item(def_id).and_then(|item| { match item.container { TraitContainer(_) => None, ImplContainer(def_id) => Some(def_id), diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 3b7c46ef7fe..638345608c2 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -543,6 +543,7 @@ pub struct ProjectionTy<'tcx> { pub struct BareFnTy<'tcx> { pub unsafety: hir::Unsafety, pub abi: abi::Abi, + /// Signature (inputs and output) of this function type. pub sig: PolyFnSig<'tcx>, } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index e6db35cc3f5..b4ac6b9d250 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -24,11 +24,11 @@ use util::nodemap::FxHashMap; use middle::lang_items; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; +use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult}; use std::cell::RefCell; use std::cmp; -use std::hash::{Hash, Hasher}; -use std::collections::hash_map::DefaultHasher; +use std::hash::Hash; use std::intrinsics; use syntax::ast::{self, Name}; use syntax::attr::{self, SignedInt, UnsignedInt}; @@ -349,7 +349,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Creates a hash of the type `Ty` which will be the same no matter what crate /// context it's calculated within. This is used by the `type_id` intrinsic. pub fn type_id_hash(self, ty: Ty<'tcx>) -> u64 { - let mut hasher = TypeIdHasher::new(self, DefaultHasher::default()); + let mut hasher = TypeIdHasher::new(self); hasher.visit_ty(ty); hasher.finish() } @@ -395,96 +395,26 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } -/// When hashing a type this ends up affecting properties like symbol names. We -/// want these symbol names to be calculated independent of other factors like -/// what architecture you're compiling *from*. -/// -/// The hashing just uses the standard `Hash` trait, but the implementations of -/// `Hash` for the `usize` and `isize` types are *not* architecture independent -/// (e.g. they has 4 or 8 bytes). As a result we want to avoid `usize` and -/// `isize` completely when hashing. To ensure that these don't leak in we use a -/// custom hasher implementation here which inflates the size of these to a `u64` -/// and `i64`. -/// -/// The same goes for endianess: We always convert multi-byte integers to little -/// endian before hashing. -#[derive(Debug)] -pub struct ArchIndependentHasher<H> { - inner: H, -} - -impl<H> ArchIndependentHasher<H> { - pub fn new(inner: H) -> ArchIndependentHasher<H> { - ArchIndependentHasher { inner: inner } - } - - pub fn into_inner(self) -> H { - self.inner - } +pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, W> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, + state: StableHasher<W>, } -impl<H: Hasher> Hasher for ArchIndependentHasher<H> { - fn write(&mut self, bytes: &[u8]) { - self.inner.write(bytes) - } - - fn finish(&self) -> u64 { - self.inner.finish() - } - - fn write_u8(&mut self, i: u8) { - self.inner.write_u8(i) - } - fn write_u16(&mut self, i: u16) { - self.inner.write_u16(i.to_le()) - } - fn write_u32(&mut self, i: u32) { - self.inner.write_u32(i.to_le()) - } - fn write_u64(&mut self, i: u64) { - self.inner.write_u64(i.to_le()) - } - fn write_usize(&mut self, i: usize) { - self.inner.write_u64((i as u64).to_le()) +impl<'a, 'gcx, 'tcx, W> TypeIdHasher<'a, 'gcx, 'tcx, W> + where W: StableHasherResult +{ + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Self { + TypeIdHasher { tcx: tcx, state: StableHasher::new() } } - fn write_i8(&mut self, i: i8) { - self.inner.write_i8(i) - } - fn write_i16(&mut self, i: i16) { - self.inner.write_i16(i.to_le()) - } - fn write_i32(&mut self, i: i32) { - self.inner.write_i32(i.to_le()) - } - fn write_i64(&mut self, i: i64) { - self.inner.write_i64(i.to_le()) - } - fn write_isize(&mut self, i: isize) { - self.inner.write_i64((i as i64).to_le()) - } -} - -pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, H> { - tcx: TyCtxt<'a, 'gcx, 'tcx>, - state: ArchIndependentHasher<H>, -} -impl<'a, 'gcx, 'tcx, H: Hasher> TypeIdHasher<'a, 'gcx, 'tcx, H> { - pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, state: H) -> Self { - TypeIdHasher { - tcx: tcx, - state: ArchIndependentHasher::new(state), - } + pub fn finish(self) -> W { + self.state.finish() } pub fn hash<T: Hash>(&mut self, x: T) { x.hash(&mut self.state); } - pub fn finish(self) -> u64 { - self.state.finish() - } - fn hash_discriminant_u8<T>(&mut self, x: &T) { let v = unsafe { intrinsics::discriminant_value(x) @@ -504,13 +434,11 @@ impl<'a, 'gcx, 'tcx, H: Hasher> TypeIdHasher<'a, 'gcx, 'tcx, H> { pub fn def_path(&mut self, def_path: &ast_map::DefPath) { def_path.deterministic_hash_to(self.tcx, &mut self.state); } - - pub fn into_inner(self) -> H { - self.state.inner - } } -impl<'a, 'gcx, 'tcx, H: Hasher> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, H> { +impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> + where W: StableHasherResult +{ fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { // Distinguish between the Ty variants uniformly. self.hash_discriminant_u8(&ty.sty); diff --git a/src/librustc_back/target/aarch64_linux_android.rs b/src/librustc_back/target/aarch64_linux_android.rs index 140195c780b..54eead94986 100644 --- a/src/librustc_back/target/aarch64_linux_android.rs +++ b/src/librustc_back/target/aarch64_linux_android.rs @@ -10,6 +10,9 @@ use target::{Target, TargetOptions, TargetResult}; +// See https://developer.android.com/ndk/guides/abis.html#arm64-v8a +// for target ABI requirements. + pub fn target() -> TargetResult { let mut base = super::android_base::opts(); base.max_atomic_width = Some(128); diff --git a/src/librustc_back/target/apple_base.rs b/src/librustc_back/target/apple_base.rs index 70c7ea99e13..21a2d4293df 100644 --- a/src/librustc_back/target/apple_base.rs +++ b/src/librustc_back/target/apple_base.rs @@ -37,6 +37,7 @@ pub fn opts() -> TargetOptions { function_sections: false, dynamic_linking: true, executables: true, + target_family: Some("unix".to_string()), is_like_osx: true, has_rpath: true, dll_prefix: "lib".to_string(), diff --git a/src/librustc_back/target/armv7_linux_androideabi.rs b/src/librustc_back/target/armv7_linux_androideabi.rs index 42f0deaa3fb..36f409b7948 100644 --- a/src/librustc_back/target/armv7_linux_androideabi.rs +++ b/src/librustc_back/target/armv7_linux_androideabi.rs @@ -1,4 +1,4 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,9 +10,12 @@ use target::{Target, TargetOptions, TargetResult}; +// See https://developer.android.com/ndk/guides/abis.html#v7a +// for target ABI requirements. + pub fn target() -> TargetResult { let mut base = super::android_base::opts(); - base.features = "+v7,+thumb2,+vfp3,+d16".to_string(); + base.features = "+v7,+thumb2,+vfp3,+d16,-neon".to_string(); base.max_atomic_width = Some(64); Ok(Target { diff --git a/src/librustc_back/target/asmjs_unknown_emscripten.rs b/src/librustc_back/target/asmjs_unknown_emscripten.rs index d86a9b09327..98a9e343d00 100644 --- a/src/librustc_back/target/asmjs_unknown_emscripten.rs +++ b/src/librustc_back/target/asmjs_unknown_emscripten.rs @@ -23,6 +23,7 @@ pub fn target() -> Result<Target, String> { obj_is_bitcode: true, max_atomic_width: Some(32), post_link_args: vec!["-s".to_string(), "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string()], + target_family: Some("unix".to_string()), .. Default::default() }; Ok(Target { diff --git a/src/librustc_back/target/bitrig_base.rs b/src/librustc_back/target/bitrig_base.rs index 7baf80066b2..62418e68d43 100644 --- a/src/librustc_back/target/bitrig_base.rs +++ b/src/librustc_back/target/bitrig_base.rs @@ -15,6 +15,7 @@ pub fn opts() -> TargetOptions { TargetOptions { dynamic_linking: true, executables: true, + target_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, position_independent_executables: true, diff --git a/src/librustc_back/target/dragonfly_base.rs b/src/librustc_back/target/dragonfly_base.rs index 7555181a15c..dca33e45af7 100644 --- a/src/librustc_back/target/dragonfly_base.rs +++ b/src/librustc_back/target/dragonfly_base.rs @@ -15,6 +15,7 @@ pub fn opts() -> TargetOptions { TargetOptions { dynamic_linking: true, executables: true, + target_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: vec![ diff --git a/src/librustc_back/target/freebsd_base.rs b/src/librustc_back/target/freebsd_base.rs index 7555181a15c..dca33e45af7 100644 --- a/src/librustc_back/target/freebsd_base.rs +++ b/src/librustc_back/target/freebsd_base.rs @@ -15,6 +15,7 @@ pub fn opts() -> TargetOptions { TargetOptions { dynamic_linking: true, executables: true, + target_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: vec![ diff --git a/src/librustc_back/target/fuchsia_base.rs b/src/librustc_back/target/fuchsia_base.rs index 69546684cb7..8c517224201 100644 --- a/src/librustc_back/target/fuchsia_base.rs +++ b/src/librustc_back/target/fuchsia_base.rs @@ -15,6 +15,7 @@ pub fn opts() -> TargetOptions { TargetOptions { dynamic_linking: true, executables: true, + target_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: vec![ diff --git a/src/librustc_back/target/haiku_base.rs b/src/librustc_back/target/haiku_base.rs index 5e319ba1838..bfdc9faaa8a 100644 --- a/src/librustc_back/target/haiku_base.rs +++ b/src/librustc_back/target/haiku_base.rs @@ -17,6 +17,7 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, has_rpath: true, + target_family: Some("unix".to_string()), linker_is_gnu: true, .. Default::default() } diff --git a/src/librustc_back/target/i686_linux_android.rs b/src/librustc_back/target/i686_linux_android.rs index a2c007d4969..f8a8f5a3500 100644 --- a/src/librustc_back/target/i686_linux_android.rs +++ b/src/librustc_back/target/i686_linux_android.rs @@ -10,6 +10,9 @@ use target::{Target, TargetResult}; +// See https://developer.android.com/ndk/guides/abis.html#x86 +// for target ABI requirements. + pub fn target() -> TargetResult { let mut base = super::android_base::opts(); diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs index d1ab71e4140..4b2ae9c8e69 100644 --- a/src/librustc_back/target/linux_base.rs +++ b/src/librustc_back/target/linux_base.rs @@ -15,6 +15,7 @@ pub fn opts() -> TargetOptions { TargetOptions { dynamic_linking: true, executables: true, + target_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: vec![ diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 351d469ea28..8c37eb6986a 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -69,6 +69,7 @@ mod windows_base; mod windows_msvc_base; mod thumb_base; mod fuchsia_base; +mod redox_base; pub type TargetResult = Result<Target, String>; @@ -184,6 +185,8 @@ supported_targets! { ("aarch64-unknown-fuchsia", aarch64_unknown_fuchsia), ("x86_64-unknown-fuchsia", x86_64_unknown_fuchsia), + ("x86_64-unknown-redox", x86_64_unknown_redox), + ("i386-apple-ios", i386_apple_ios), ("x86_64-apple-ios", x86_64_apple_ios), ("aarch64-apple-ios", aarch64_apple_ios), @@ -267,6 +270,9 @@ pub struct TargetOptions { /// user-defined libraries. pub post_link_args: Vec<String>, + /// Extra arguments to pass to the external assembler (when used) + pub asm_args: Vec<String>, + /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults /// to "generic". pub cpu: String, @@ -394,6 +400,7 @@ impl Default for TargetOptions { ar: option_env!("CFG_DEFAULT_AR").unwrap_or("ar").to_string(), pre_link_args: Vec::new(), post_link_args: Vec::new(), + asm_args: Vec::new(), cpu: "generic".to_string(), features: "".to_string(), dynamic_linking: false, @@ -561,6 +568,7 @@ impl Target { key!(late_link_args, list); key!(post_link_objects, list); key!(post_link_args, list); + key!(asm_args, list); key!(cpu); key!(features); key!(dynamic_linking, bool); @@ -723,6 +731,7 @@ impl ToJson for Target { target_option_val!(late_link_args); target_option_val!(post_link_objects); target_option_val!(post_link_args); + target_option_val!(asm_args); target_option_val!(cpu); target_option_val!(features); target_option_val!(dynamic_linking); diff --git a/src/librustc_back/target/netbsd_base.rs b/src/librustc_back/target/netbsd_base.rs index 6e038a7ed56..57179a68afd 100644 --- a/src/librustc_back/target/netbsd_base.rs +++ b/src/librustc_back/target/netbsd_base.rs @@ -15,6 +15,7 @@ pub fn opts() -> TargetOptions { TargetOptions { dynamic_linking: true, executables: true, + target_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: vec![ diff --git a/src/librustc_back/target/openbsd_base.rs b/src/librustc_back/target/openbsd_base.rs index 1f74170e399..12b8e8bdc88 100644 --- a/src/librustc_back/target/openbsd_base.rs +++ b/src/librustc_back/target/openbsd_base.rs @@ -15,6 +15,7 @@ pub fn opts() -> TargetOptions { TargetOptions { dynamic_linking: true, executables: true, + target_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, is_like_openbsd: true, diff --git a/src/librustc_back/target/redox_base.rs b/src/librustc_back/target/redox_base.rs new file mode 100644 index 00000000000..1dffff59809 --- /dev/null +++ b/src/librustc_back/target/redox_base.rs @@ -0,0 +1,50 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use PanicStrategy; +use target::TargetOptions; +use std::default::Default; + +pub fn opts() -> TargetOptions { + TargetOptions { + pre_link_args: vec![ + // We want to be able to strip as much executable code as possible + // from the linker command line, and this flag indicates to the + // linker that it can avoid linking in dynamic libraries that don't + // actually satisfy any symbols up to that point (as with many other + // resolutions the linker does). This option only applies to all + // following libraries so we're sure to pass it as one of the first + // arguments. + "-Wl,--as-needed".to_string(), + + // Always enable NX protection when it is available + "-Wl,-z,noexecstack".to_string(), + + // Static link + "-static".to_string() + ], + late_link_args: vec![ + "-lc".to_string(), + "-lm".to_string() + ], + executables: true, + relocation_model: "static".to_string(), + disable_redzone: true, + eliminate_frame_pointer: false, + target_family: None, + linker_is_gnu: true, + no_default_libraries: true, + lib_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: "alloc_system".to_string(), + has_elf_tls: true, + panic_strategy: PanicStrategy::Abort, + .. Default::default() + } +} diff --git a/src/librustc_back/target/solaris_base.rs b/src/librustc_back/target/solaris_base.rs index a7af0462e57..41323c9c26b 100644 --- a/src/librustc_back/target/solaris_base.rs +++ b/src/librustc_back/target/solaris_base.rs @@ -16,6 +16,7 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, executables: true, has_rpath: true, + target_family: Some("unix".to_string()), is_like_solaris: true, exe_allocation_crate: super::maybe_jemalloc(), diff --git a/src/librustc_back/target/wasm32_unknown_emscripten.rs b/src/librustc_back/target/wasm32_unknown_emscripten.rs index 77ab4fcae70..fec269074da 100644 --- a/src/librustc_back/target/wasm32_unknown_emscripten.rs +++ b/src/librustc_back/target/wasm32_unknown_emscripten.rs @@ -26,6 +26,7 @@ pub fn target() -> Result<Target, String> { max_atomic_width: Some(32), post_link_args: vec!["-s".to_string(), "BINARYEN=1".to_string(), "-s".to_string(), "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string()], + target_family: Some("unix".to_string()), .. Default::default() }; Ok(Target { diff --git a/src/librustc_back/target/windows_base.rs b/src/librustc_back/target/windows_base.rs index 19ca0df51b9..db02e142fcc 100644 --- a/src/librustc_back/target/windows_base.rs +++ b/src/librustc_back/target/windows_base.rs @@ -24,6 +24,7 @@ pub fn opts() -> TargetOptions { staticlib_prefix: "".to_string(), staticlib_suffix: ".lib".to_string(), no_default_libraries: true, + target_family: Some("windows".to_string()), is_like_windows: true, allows_weak_linkage: false, pre_link_args: vec![ diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs index 84e22e84fdb..efa215b419d 100644 --- a/src/librustc_back/target/windows_msvc_base.rs +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -53,6 +53,7 @@ pub fn opts() -> TargetOptions { exe_suffix: ".exe".to_string(), staticlib_prefix: "".to_string(), staticlib_suffix: ".lib".to_string(), + target_family: Some("windows".to_string()), is_like_windows: true, is_like_msvc: true, pre_link_args: vec![ diff --git a/src/librustc_back/target/x86_64_unknown_redox.rs b/src/librustc_back/target/x86_64_unknown_redox.rs new file mode 100644 index 00000000000..cecac06b235 --- /dev/null +++ b/src/librustc_back/target/x86_64_unknown_redox.rs @@ -0,0 +1,30 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use target::{Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::redox_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.push("-m64".to_string()); + + Ok(Target { + llvm_target: "x86_64-unknown-redox".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), + arch: "x86_64".to_string(), + target_os: "redox".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + options: base, + }) +} diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 4f49bfc9725..88e5bae483d 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -481,54 +481,55 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { is_cleanup: bool) -> Vec<BasicBlock> { - let mut succ = succ; let mut unwind_succ = if is_cleanup { None } else { c.unwind }; - let mut update_drop_flag = true; + + let mut succ = self.new_block( + c, c.is_cleanup, TerminatorKind::Goto { target: succ } + ); + + // Always clear the "master" drop flag at the bottom of the + // ladder. This is needed because the "master" drop flag + // protects the ADT's discriminant, which is invalidated + // after the ADT is dropped. + self.set_drop_flag( + Location { block: succ, statement_index: 0 }, + c.path, + DropFlagState::Absent + ); fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| { - let drop_block = match path { - Some(path) => { - debug!("drop_ladder: for std field {} ({:?})", i, lv); - - self.elaborated_drop_block(&DropCtxt { - source_info: c.source_info, - is_cleanup: is_cleanup, - init_data: c.init_data, - lvalue: lv, - path: path, - succ: succ, - unwind: unwind_succ, - }) - } - None => { - debug!("drop_ladder: for rest field {} ({:?})", i, lv); - - let blk = self.complete_drop(&DropCtxt { - source_info: c.source_info, - is_cleanup: is_cleanup, - init_data: c.init_data, - lvalue: lv, - path: c.path, - succ: succ, - unwind: unwind_succ, - }, update_drop_flag); - - // the drop flag has been updated - updating - // it again would clobber it. - update_drop_flag = false; - - blk - } + succ = if let Some(path) = path { + debug!("drop_ladder: for std field {} ({:?})", i, lv); + + self.elaborated_drop_block(&DropCtxt { + source_info: c.source_info, + is_cleanup: is_cleanup, + init_data: c.init_data, + lvalue: lv, + path: path, + succ: succ, + unwind: unwind_succ, + }) + } else { + debug!("drop_ladder: for rest field {} ({:?})", i, lv); + + self.complete_drop(&DropCtxt { + source_info: c.source_info, + is_cleanup: is_cleanup, + init_data: c.init_data, + lvalue: lv, + path: c.path, + succ: succ, + unwind: unwind_succ, + }, false) }; - succ = drop_block; unwind_succ = unwind_ladder.as_ref().map(|p| p[i]); - - drop_block + succ }).collect() } diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 23771f4bae3..ebe10349011 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -324,7 +324,6 @@ impl Witness { let v = ctor.variant_for_adt(adt); let qpath = hir::QPath::Resolved(None, P(hir::Path { span: DUMMY_SP, - global: false, def: Def::Err, segments: vec![hir::PathSegment::from_name(v.name)].into(), })); diff --git a/src/librustc_data_structures/base_n.rs b/src/librustc_data_structures/base_n.rs index bf3e682f86f..4359581a897 100644 --- a/src/librustc_data_structures/base_n.rs +++ b/src/librustc_data_structures/base_n.rs @@ -14,6 +14,8 @@ use std::str; pub const MAX_BASE: u64 = 64; +pub const ALPHANUMERIC_ONLY: u64 = 62; + const BASE_64: &'static [u8; MAX_BASE as usize] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@$"; diff --git a/src/librustc_data_structures/flock.rs b/src/librustc_data_structures/flock.rs index 510c9ceef09..33d71ba8626 100644 --- a/src/librustc_data_structures/flock.rs +++ b/src/librustc_data_structures/flock.rs @@ -31,6 +31,7 @@ mod imp { mod os { use libc; + #[repr(C)] pub struct flock { pub l_type: libc::c_short, pub l_whence: libc::c_short, @@ -53,6 +54,7 @@ mod imp { mod os { use libc; + #[repr(C)] pub struct flock { pub l_start: libc::off_t, pub l_len: libc::off_t, @@ -76,6 +78,7 @@ mod imp { mod os { use libc; + #[repr(C)] pub struct flock { pub l_start: libc::off_t, pub l_len: libc::off_t, @@ -98,6 +101,7 @@ mod imp { mod os { use libc; + #[repr(C)] pub struct flock { pub l_type: libc::c_short, pub l_whence: libc::c_short, @@ -119,6 +123,7 @@ mod imp { mod os { use libc; + #[repr(C)] pub struct flock { pub l_start: libc::off_t, pub l_len: libc::off_t, @@ -141,6 +146,7 @@ mod imp { mod os { use libc; + #[repr(C)] pub struct flock { pub l_type: libc::c_short, pub l_whence: libc::c_short, diff --git a/src/librustc_data_structures/graph/tests.rs b/src/librustc_data_structures/graph/tests.rs index a87410e6e1c..bdefc39a61a 100644 --- a/src/librustc_data_structures/graph/tests.rs +++ b/src/librustc_data_structures/graph/tests.rs @@ -11,8 +11,6 @@ use graph::*; use std::fmt::Debug; -type TestNode = Node<&'static str>; -type TestEdge = Edge<&'static str>; type TestGraph = Graph<&'static str, &'static str>; fn create_graph() -> TestGraph { diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index de13b9bf4be..86f244d65dd 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -44,6 +44,8 @@ extern crate serialize as rustc_serialize; // used by deriving #[cfg(unix)] extern crate libc; +pub use rustc_serialize::hex::ToHex; + pub mod array_vec; pub mod accumulate_vec; pub mod small_vec; @@ -59,6 +61,7 @@ pub mod indexed_vec; pub mod obligation_forest; pub mod snapshot_map; pub mod snapshot_vec; +pub mod stable_hasher; pub mod transitive_relation; pub mod unify; pub mod fnv; diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs new file mode 100644 index 00000000000..ed97c3dde5e --- /dev/null +++ b/src/librustc_data_structures/stable_hasher.rs @@ -0,0 +1,176 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::hash::Hasher; +use std::marker::PhantomData; +use std::mem; +use blake2b::Blake2bHasher; +use rustc_serialize::leb128; + +fn write_unsigned_leb128_to_buf(buf: &mut [u8; 16], value: u64) -> usize { + leb128::write_unsigned_leb128_to(value, |i, v| buf[i] = v) +} + +fn write_signed_leb128_to_buf(buf: &mut [u8; 16], value: i64) -> usize { + leb128::write_signed_leb128_to(value, |i, v| buf[i] = v) +} + +/// When hashing something that ends up affecting properties like symbol names. We +/// want these symbol names to be calculated independent of other factors like +/// what architecture you're compiling *from*. +/// +/// The hashing just uses the standard `Hash` trait, but the implementations of +/// `Hash` for the `usize` and `isize` types are *not* architecture independent +/// (e.g. they has 4 or 8 bytes). As a result we want to avoid `usize` and +/// `isize` completely when hashing. +/// +/// To do that, we encode all integers to be hashed with some +/// arch-independent encoding. +/// +/// At the moment, we pass i8/u8 straight through and encode +/// all other integers using leb128. +/// +/// This hasher currently always uses the stable Blake2b algorithm +/// and allows for variable output lengths through its type +/// parameter. +#[derive(Debug)] +pub struct StableHasher<W> { + state: Blake2bHasher, + bytes_hashed: u64, + width: PhantomData<W>, +} + +pub trait StableHasherResult: Sized { + fn finish(hasher: StableHasher<Self>) -> Self; +} + +impl<W: StableHasherResult> StableHasher<W> { + pub fn new() -> Self { + StableHasher { + state: Blake2bHasher::new(mem::size_of::<W>(), &[]), + bytes_hashed: 0, + width: PhantomData, + } + } + + pub fn finish(self) -> W { + W::finish(self) + } +} + +impl StableHasherResult for [u8; 20] { + fn finish(mut hasher: StableHasher<Self>) -> Self { + let mut result: [u8; 20] = [0; 20]; + result.copy_from_slice(hasher.state.finalize()); + result + } +} + +impl StableHasherResult for u64 { + fn finish(mut hasher: StableHasher<Self>) -> Self { + hasher.state.finalize(); + hasher.state.finish() + } +} + +impl<W> StableHasher<W> { + #[inline] + pub fn finalize(&mut self) -> &[u8] { + self.state.finalize() + } + + #[inline] + pub fn bytes_hashed(&self) -> u64 { + self.bytes_hashed + } + + #[inline] + fn write_uleb128(&mut self, value: u64) { + let mut buf = [0; 16]; + let len = write_unsigned_leb128_to_buf(&mut buf, value); + self.state.write(&buf[..len]); + self.bytes_hashed += len as u64; + } + + #[inline] + fn write_ileb128(&mut self, value: i64) { + let mut buf = [0; 16]; + let len = write_signed_leb128_to_buf(&mut buf, value); + self.state.write(&buf[..len]); + self.bytes_hashed += len as u64; + } +} + +// For the non-u8 integer cases we leb128 encode them first. Because small +// integers dominate, this significantly and cheaply reduces the number of +// bytes hashed, which is good because blake2b is expensive. +impl<W> Hasher for StableHasher<W> { + fn finish(&self) -> u64 { + panic!("use StableHasher::finish instead"); + } + + #[inline] + fn write(&mut self, bytes: &[u8]) { + self.state.write(bytes); + self.bytes_hashed += bytes.len() as u64; + } + + #[inline] + fn write_u8(&mut self, i: u8) { + self.state.write_u8(i); + self.bytes_hashed += 1; + } + + #[inline] + fn write_u16(&mut self, i: u16) { + self.write_uleb128(i as u64); + } + + #[inline] + fn write_u32(&mut self, i: u32) { + self.write_uleb128(i as u64); + } + + #[inline] + fn write_u64(&mut self, i: u64) { + self.write_uleb128(i); + } + + #[inline] + fn write_usize(&mut self, i: usize) { + self.write_uleb128(i as u64); + } + + #[inline] + fn write_i8(&mut self, i: i8) { + self.state.write_i8(i); + self.bytes_hashed += 1; + } + + #[inline] + fn write_i16(&mut self, i: i16) { + self.write_ileb128(i as i64); + } + + #[inline] + fn write_i32(&mut self, i: i32) { + self.write_ileb128(i as i64); + } + + #[inline] + fn write_i64(&mut self, i: i64) { + self.write_ileb128(i); + } + + #[inline] + fn write_isize(&mut self, i: isize) { + self.write_ileb128(i as i64); + } +} diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 8d995e97b95..8da9a23f4fa 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -8,12 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::hir; -use rustc::hir::{map as hir_map, FreevarMap, TraitMap}; +use rustc::hir::{self, map as hir_map}; use rustc::hir::lowering::lower_crate; -use rustc_data_structures::blake2b::Blake2bHasher; -use rustc_data_structures::fmt_wrap::FmtWrap; -use rustc::ty::util::ArchIndependentHasher; +use rustc_data_structures::stable_hasher::StableHasher; use rustc_mir as mir; use rustc::session::{Session, CompileResult, compile_result_from_err_count}; use rustc::session::config::{self, Input, OutputFilenames, OutputType, @@ -22,11 +19,12 @@ use rustc::session::search_paths::PathKind; use rustc::lint; use rustc::middle::{self, dependency_format, stability, reachable}; use rustc::middle::privacy::AccessLevels; -use rustc::ty::{self, TyCtxt}; +use rustc::ty::{self, TyCtxt, Resolutions}; use rustc::util::common::time; use rustc::util::nodemap::{NodeSet, NodeMap}; use rustc_borrowck as borrowck; use rustc_incremental::{self, IncrementalHashesMap}; +use rustc_incremental::ich::Fingerprint; use rustc_resolve::{MakeGlobMap, Resolver}; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::CStore; @@ -60,13 +58,6 @@ use syntax_ext; use derive_registrar; -#[derive(Clone)] -pub struct Resolutions { - pub freevars: FreevarMap, - pub trait_map: TraitMap, - pub maybe_unused_trait_imports: NodeSet, -} - pub fn compile_input(sess: &Session, cstore: &CStore, input: &Input, @@ -601,6 +592,7 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session, } }); + let whitelisted_legacy_custom_derives = registry.take_whitelisted_custom_derives(); let Registry { syntax_exts, early_lint_passes, late_lint_passes, lint_groups, llvm_passes, attributes, mir_passes, .. } = registry; @@ -640,6 +632,7 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session, let resolver_arenas = Resolver::arenas(); let mut resolver = Resolver::new(sess, &krate, make_glob_map, &mut crate_loader, &resolver_arenas); + resolver.whitelisted_legacy_custom_derives = whitelisted_legacy_custom_derives; syntax_ext::register_builtins(&mut resolver, syntax_exts, sess.features.borrow().quote); krate = time(time_passes, "expansion", || { @@ -865,11 +858,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, TyCtxt::create_and_enter(sess, arenas, - resolutions.trait_map, + resolutions, named_region_map, hir_map, - resolutions.freevars, - resolutions.maybe_unused_trait_imports, region_map, lang_items, index, @@ -1274,7 +1265,7 @@ pub fn compute_crate_disambiguator(session: &Session) -> String { // FIXME(mw): It seems that the crate_disambiguator is used everywhere as // a hex-string instead of raw bytes. We should really use the // smaller representation. - let mut hasher = ArchIndependentHasher::new(Blake2bHasher::new(128 / 8, &[])); + let mut hasher = StableHasher::<Fingerprint>::new(); let mut metadata = session.opts.cg.metadata.clone(); // We don't want the crate_disambiguator to dependent on the order @@ -1292,14 +1283,11 @@ pub fn compute_crate_disambiguator(session: &Session) -> String { hasher.write(s.as_bytes()); } - let mut hash_state = hasher.into_inner(); - let hash_bytes = hash_state.finalize(); - // If this is an executable, add a special suffix, so that we don't get // symbol conflicts when linking against a library of the same name. let is_exe = session.crate_types.borrow().contains(&config::CrateTypeExecutable); - format!("{:x}{}", FmtWrap(hash_bytes), if is_exe { "-exe" } else {""}) + format!("{}{}", hasher.finish().to_hex(), if is_exe { "-exe" } else {""}) } pub fn build_output_filenames(input: &Input, diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index b055b043723..74df1e52bde 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -15,10 +15,9 @@ pub use self::PpSourceMode::*; pub use self::PpMode::*; use self::NodesMatchingUII::*; -use abort_on_err; -use driver::{self, Resolutions}; +use {abort_on_err, driver}; -use rustc::ty::{self, TyCtxt}; +use rustc::ty::{self, TyCtxt, Resolutions}; use rustc::cfg; use rustc::cfg::graphviz::LabelledCFG; use rustc::dep_graph::DepGraph; diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 2f8550e5acd..cbab39c3908 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -138,11 +138,9 @@ fn test_env<F>(source_string: &str, let index = stability::Index::new(&ast_map); TyCtxt::create_and_enter(&sess, &arenas, - resolutions.trait_map, + resolutions, named_region_map.unwrap(), ast_map, - resolutions.freevars, - resolutions.maybe_unused_trait_imports, region_map, lang_items, index, diff --git a/src/librustc_incremental/calculate_svh/hasher.rs b/src/librustc_incremental/calculate_svh/hasher.rs deleted file mode 100644 index d7d9c231a91..00000000000 --- a/src/librustc_incremental/calculate_svh/hasher.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::mem; -use std::hash::Hasher; -use rustc_data_structures::blake2b::Blake2bHasher; -use rustc::ty::util::ArchIndependentHasher; -use ich::Fingerprint; -use rustc_serialize::leb128::write_unsigned_leb128; - -#[derive(Debug)] -pub struct IchHasher { - state: ArchIndependentHasher<Blake2bHasher>, - leb128_helper: Vec<u8>, - bytes_hashed: u64, -} - -impl IchHasher { - pub fn new() -> IchHasher { - let hash_size = mem::size_of::<Fingerprint>(); - IchHasher { - state: ArchIndependentHasher::new(Blake2bHasher::new(hash_size, &[])), - leb128_helper: vec![], - bytes_hashed: 0 - } - } - - pub fn bytes_hashed(&self) -> u64 { - self.bytes_hashed - } - - pub fn finish(self) -> Fingerprint { - let mut fingerprint = Fingerprint::zero(); - fingerprint.0.copy_from_slice(self.state.into_inner().finalize()); - fingerprint - } - - #[inline] - fn write_uleb128(&mut self, value: u64) { - let len = write_unsigned_leb128(&mut self.leb128_helper, 0, value); - self.state.write(&self.leb128_helper[0..len]); - self.bytes_hashed += len as u64; - } -} - -// For the non-u8 integer cases we leb128 encode them first. Because small -// integers dominate, this significantly and cheaply reduces the number of -// bytes hashed, which is good because blake2b is expensive. -impl Hasher for IchHasher { - fn finish(&self) -> u64 { - bug!("Use other finish() implementation to get the full 128-bit hash."); - } - - #[inline] - fn write(&mut self, bytes: &[u8]) { - self.state.write(bytes); - self.bytes_hashed += bytes.len() as u64; - } - - // There is no need to leb128-encode u8 values. - - #[inline] - fn write_u16(&mut self, i: u16) { - self.write_uleb128(i as u64); - } - - #[inline] - fn write_u32(&mut self, i: u32) { - self.write_uleb128(i as u64); - } - - #[inline] - fn write_u64(&mut self, i: u64) { - self.write_uleb128(i); - } - - #[inline] - fn write_usize(&mut self, i: usize) { - self.write_uleb128(i as u64); - } -} diff --git a/src/librustc_incremental/calculate_svh/mod.rs b/src/librustc_incremental/calculate_svh/mod.rs index df65c4d2794..eb31be4a8ad 100644 --- a/src/librustc_incremental/calculate_svh/mod.rs +++ b/src/librustc_incremental/calculate_svh/mod.rs @@ -36,6 +36,8 @@ use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; use rustc::hir::intravisit as visit; use rustc::hir::intravisit::{Visitor, NestedVisitorMap}; use rustc::ty::TyCtxt; +use rustc_data_structures::stable_hasher::StableHasher; +use ich::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc::util::common::record_time; use rustc::session::config::DebugInfoLevel::NoDebugInfo; @@ -43,14 +45,12 @@ use rustc::session::config::DebugInfoLevel::NoDebugInfo; use self::def_path_hash::DefPathHashes; use self::svh_visitor::StrictVersionHashVisitor; use self::caching_codemap_view::CachingCodemapView; -use self::hasher::IchHasher; -use ich::Fingerprint; - mod def_path_hash; mod svh_visitor; mod caching_codemap_view; -pub mod hasher; + +pub type IchHasher = StableHasher<Fingerprint>; pub struct IncrementalHashesMap { hashes: FxHashMap<DepNode<DefId>, Fingerprint>, @@ -244,4 +244,3 @@ impl<'a, 'tcx> Visitor<'tcx> for HashItemsVisitor<'a, 'tcx> { visit::walk_foreign_item(self, item); } } - diff --git a/src/librustc_incremental/calculate_svh/svh_visitor.rs b/src/librustc_incremental/calculate_svh/svh_visitor.rs index de52b70f1ec..ccae5f3b003 100644 --- a/src/librustc_incremental/calculate_svh/svh_visitor.rs +++ b/src/librustc_incremental/calculate_svh/svh_visitor.rs @@ -28,11 +28,11 @@ use rustc::hir::def_id::DefId; use rustc::hir::intravisit as visit; use rustc::ty::TyCtxt; use rustc_data_structures::fnv; -use std::hash::Hash; +use std::hash::{Hash, Hasher}; use super::def_path_hash::DefPathHashes; use super::caching_codemap_view::CachingCodemapView; -use super::hasher::IchHasher; +use super::IchHasher; const IGNORED_ATTRIBUTES: &'static [&'static str] = &[ "cfg", @@ -189,7 +189,6 @@ enum SawAbiComponent<'a> { SawStructField, SawVariant, SawQPath, - SawPath(bool), SawPathSegment, SawPathParameters, SawBlock, @@ -265,7 +264,7 @@ enum SawExprComponent<'a> { SawExprPath, SawExprAddrOf(hir::Mutability), SawExprRet, - SawExprInlineAsm(&'a hir::InlineAsm), + SawExprInlineAsm(StableInlineAsm<'a>), SawExprStruct, SawExprRepeat, } @@ -341,7 +340,7 @@ fn saw_expr<'a>(node: &'a Expr_, ExprBreak(label, _) => (SawExprBreak(label.map(|l| l.name.as_str())), false), ExprAgain(label) => (SawExprAgain(label.map(|l| l.name.as_str())), false), ExprRet(..) => (SawExprRet, false), - ExprInlineAsm(ref a,..) => (SawExprInlineAsm(a), false), + ExprInlineAsm(ref a,..) => (SawExprInlineAsm(StableInlineAsm(a)), false), ExprStruct(..) => (SawExprStruct, false), ExprRepeat(..) => (SawExprRepeat, false), } @@ -492,6 +491,46 @@ enum SawSpanExpnKind { SomeExpansion, } +/// A wrapper that provides a stable Hash implementation. +struct StableInlineAsm<'a>(&'a InlineAsm); + +impl<'a> Hash for StableInlineAsm<'a> { + fn hash<H: Hasher>(&self, state: &mut H) { + let InlineAsm { + asm, + asm_str_style, + ref outputs, + ref inputs, + ref clobbers, + volatile, + alignstack, + dialect, + expn_id: _, // This is used for error reporting + } = *self.0; + + asm.as_str().hash(state); + asm_str_style.hash(state); + outputs.len().hash(state); + for output in outputs { + let InlineAsmOutput { constraint, is_rw, is_indirect } = *output; + constraint.as_str().hash(state); + is_rw.hash(state); + is_indirect.hash(state); + } + inputs.len().hash(state); + for input in inputs { + input.as_str().hash(state); + } + clobbers.len().hash(state); + for clobber in clobbers { + clobber.as_str().hash(state); + } + volatile.hash(state); + alignstack.hash(state); + dialect.hash(state); + } +} + macro_rules! hash_attrs { ($visitor:expr, $attrs:expr) => ({ let attrs = $attrs; @@ -678,7 +717,6 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has fn visit_path(&mut self, path: &'tcx Path, _: ast::NodeId) { debug!("visit_path: st={:?}", self.st); - SawPath(path.global).hash(self.st); hash_span!(self, path.span); visit::walk_path(self, path) } diff --git a/src/librustc_incremental/ich/fingerprint.rs b/src/librustc_incremental/ich/fingerprint.rs index 005ac3896ce..d296d8293fb 100644 --- a/src/librustc_incremental/ich/fingerprint.rs +++ b/src/librustc_incremental/ich/fingerprint.rs @@ -9,6 +9,8 @@ // except according to those terms. use rustc_serialize::{Encodable, Decodable, Encoder, Decoder}; +use rustc_data_structures::stable_hasher; +use rustc_data_structures::ToHex; const FINGERPRINT_LENGTH: usize = 16; @@ -44,6 +46,10 @@ impl Fingerprint { ((self.0[6] as u64) << 48) | ((self.0[7] as u64) << 56) } + + pub fn to_hex(&self) -> String { + self.0.to_hex() + } } impl Encodable for Fingerprint { @@ -79,3 +85,12 @@ impl ::std::fmt::Display for Fingerprint { Ok(()) } } + + +impl stable_hasher::StableHasherResult for Fingerprint { + fn finish(mut hasher: stable_hasher::StableHasher<Self>) -> Self { + let mut fingerprint = Fingerprint::zero(); + fingerprint.0.copy_from_slice(hasher.finalize()); + fingerprint + } +} diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs index 3cb5244413b..ce73b14ef2d 100644 --- a/src/librustc_incremental/lib.rs +++ b/src/librustc_incremental/lib.rs @@ -48,7 +48,7 @@ pub mod ich; pub use assert_dep_graph::assert_dep_graph; pub use calculate_svh::compute_incremental_hashes_map; pub use calculate_svh::IncrementalHashesMap; -pub use calculate_svh::hasher::IchHasher; +pub use calculate_svh::IchHasher; pub use persist::load_dep_graph; pub use persist::save_dep_graph; pub use persist::save_trans_partition; diff --git a/src/librustc_incremental/persist/hash.rs b/src/librustc_incremental/persist/hash.rs index e5203ea02b4..799cb6c5e3d 100644 --- a/src/librustc_incremental/persist/hash.rs +++ b/src/librustc_incremental/persist/hash.rs @@ -66,11 +66,6 @@ impl<'a, 'tcx> HashContext<'a, 'tcx> { def_id, self.tcx.item_path_str(def_id)); - assert!(!self.tcx.map.is_inlined_def_id(def_id), - "cannot hash HIR for inlined def-id {:?} => {:?}", - def_id, - self.tcx.item_path_str(def_id)); - Some(self.incremental_hashes_map[dep_node]) } diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index 1ce4bf7f033..f3bbd02dffa 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -30,7 +30,7 @@ use super::preds::*; use super::fs::*; use super::dirty_clean; use super::file_format; -use calculate_svh::hasher::IchHasher; +use calculate_svh::IchHasher; pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, incremental_hashes_map: &IncrementalHashesMap, diff --git a/src/librustc_lint/bad_style.rs b/src/librustc_lint/bad_style.rs index 2aa74407afc..1d384741d96 100644 --- a/src/librustc_lint/bad_style.rs +++ b/src/librustc_lint/bad_style.rs @@ -382,7 +382,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonUpperCaseGlobals { fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) { // Lint for constants that look like binding identifiers (#7526) if let PatKind::Path(hir::QPath::Resolved(None, ref path)) = p.node { - if !path.global && path.segments.len() == 1 && path.segments[0].parameters.is_empty() { + if path.segments.len() == 1 && path.segments[0].parameters.is_empty() { if let Def::Const(..) = path.def { NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern", diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 6c9a3e99a04..a53d43b2a25 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -234,6 +234,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(LEGACY_DIRECTORY_OWNERSHIP), reference: "issue #37872 <https://github.com/rust-lang/rust/issues/37872>", }, + FutureIncompatibleInfo { + id: LintId::of(LEGACY_IMPORTS), + reference: "issue #38260 <https://github.com/rust-lang/rust/issues/38260>", + }, ]); // Register renamed and removed lints diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 5e9fdbfa073..751c9c3440f 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -750,7 +750,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { if let Layout::General { ref variants, ref size, discr, .. } = *layout { let discr_size = Primitive::Int(discr).size(&cx.tcx.data_layout).bytes(); - debug!("enum `{}` is {} bytes large", t, size.bytes()); + debug!("enum `{}` is {} bytes large with layout:\n{:#?}", + t, size.bytes(), layout); let (largest, slargest, largest_index) = enum_definition.variants .iter() diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs index 50bc3e7b624..e681a81cf0c 100644 --- a/src/librustc_llvm/build.rs +++ b/src/librustc_llvm/build.rs @@ -95,7 +95,8 @@ fn main() { let is_crossed = target != host; let optional_components = - ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", "systemz", "jsbackend", "msp430"]; + ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", "systemz", "jsbackend", "msp430", + "sparc"]; // FIXME: surely we don't need all these components, right? Stuff like mcjit // or interpreter the compiler itself never uses. @@ -230,6 +231,13 @@ fn main() { } } + // OpenBSD has a particular C++ runtime library name + let stdcppname = if target.contains("openbsd") { + "estdc++" + } else { + "stdc++" + }; + // C++ runtime library if !target.contains("msvc") { if let Some(s) = env::var_os("LLVM_STATIC_STDCPP") { @@ -237,11 +245,11 @@ fn main() { let path = PathBuf::from(s); println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); - println!("cargo:rustc-link-lib=static=stdc++"); + println!("cargo:rustc-link-lib=static={}", stdcppname); } else if cxxflags.contains("stdlib=libc++") { println!("cargo:rustc-link-lib=c++"); } else { - println!("cargo:rustc-link-lib=stdc++"); + println!("cargo:rustc-link-lib={}", stdcppname); } } } diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index a6b2fe471df..f3dbac7ce68 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -117,6 +117,7 @@ pub enum Attribute { StructRet = 16, UWTable = 17, ZExt = 18, + InReg = 19, } /// LLVMIntPredicate @@ -710,6 +711,7 @@ extern "C" { // Operations on instructions pub fn LLVMGetInstructionParent(Inst: ValueRef) -> BasicBlockRef; + pub fn LLVMGetFirstBasicBlock(Fn: ValueRef) -> BasicBlockRef; pub fn LLVMGetFirstInstruction(BB: BasicBlockRef) -> ValueRef; pub fn LLVMInstructionEraseFromParent(Inst: ValueRef); @@ -1417,7 +1419,8 @@ extern "C" { Ty: DIType, isLocalToUnit: bool, Val: ValueRef, - Decl: DIDescriptor) + Decl: DIDescriptor, + AlignInBits: u64) -> DIGlobalVariable; pub fn LLVMRustDIBuilderCreateVariable(Builder: DIBuilderRef, @@ -1429,7 +1432,8 @@ extern "C" { Ty: DIType, AlwaysPreserve: bool, Flags: DIFlags, - ArgNo: c_uint) + ArgNo: c_uint, + AlignInBits: u64) -> DIVariable; pub fn LLVMRustDIBuilderCreateArrayType(Builder: DIBuilderRef, diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index a15edcd44be..69709f72b8b 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -270,7 +270,8 @@ pub fn mk_section_iter(llof: ObjectFileRef) -> SectionIter { /// Safe wrapper around `LLVMGetParam`, because segfaults are no fun. pub fn get_param(llfn: ValueRef, index: c_uint) -> ValueRef { unsafe { - assert!(index < LLVMCountParams(llfn)); + assert!(index < LLVMCountParams(llfn), + "out of bounds argument access: {} out of {} arguments", index, LLVMCountParams(llfn)); LLVMGetParam(llfn, index) } } @@ -370,6 +371,12 @@ pub fn initialize_available_targets() { LLVMInitializeMSP430Target, LLVMInitializeMSP430TargetMC, LLVMInitializeMSP430AsmPrinter); + init_target!(llvm_component = "sparc", + LLVMInitializeSparcTargetInfo, + LLVMInitializeSparcTarget, + LLVMInitializeSparcTargetMC, + LLVMInitializeSparcAsmPrinter, + LLVMInitializeSparcAsmParser); } pub fn last_error() -> Option<String> { diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index 6598b7dcc52..926c44824ce 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -102,8 +102,6 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for SideTableEncodingIdVisitor<'a, 'b, 'tcx> { /// ast-map. pub fn decode_inlined_item<'a, 'tcx>(cdata: &CrateMetadata, tcx: TyCtxt<'a, 'tcx, 'tcx>, - parent_def_path: ast_map::DefPath, - parent_did: DefId, ast: Ast<'tcx>, orig_did: DefId) -> &'tcx InlinedItem { @@ -120,17 +118,9 @@ pub fn decode_inlined_item<'a, 'tcx>(cdata: &CrateMetadata, let ii = ast.item.decode((cdata, tcx, id_ranges)); let item_node_id = tcx.sess.next_node_id(); let ii = ast_map::map_decoded_item(&tcx.map, - parent_def_path, - parent_did, ii, item_node_id); - let inlined_did = tcx.map.local_def_id(item_node_id); - let ty = tcx.item_type(orig_did); - let generics = tcx.item_generics(orig_did); - tcx.item_types.borrow_mut().insert(inlined_did, ty); - tcx.generics.borrow_mut().insert(inlined_did, generics); - for (id, entry) in ast.side_tables.decode((cdata, tcx, id_ranges)) { match entry { TableEntry::TypeRelativeDef(def) => { diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index d36242537b8..a9af4118c59 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -22,6 +22,7 @@ use rustc_back::PanicStrategy; use rustc::session::search_paths::PathKind; use rustc::middle; use rustc::middle::cstore::{CrateStore, validate_crate_name, ExternCrate}; +use rustc::util::common::record_time; use rustc::util::nodemap::FxHashSet; use rustc::middle::cstore::NativeLibrary; use rustc::hir::map::Definitions; @@ -297,10 +298,14 @@ impl<'a> CrateLoader<'a> { let cnum_map = self.resolve_crate_deps(root, &crate_root, &metadata, cnum, span, dep_kind); + let def_path_table = record_time(&self.sess.perf_stats.decode_def_path_tables_time, || { + crate_root.def_path_table.decode(&metadata) + }); + let mut cmeta = cstore::CrateMetadata { name: name, extern_crate: Cell::new(None), - key_map: metadata.load_key_map(crate_root.index), + def_path_table: def_path_table, proc_macros: crate_root.macro_derive_registrar.map(|_| { self.load_derive_macros(&crate_root, dylib.clone().map(|p| p.0), span) }), diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 7700ebde181..7ec847d24cf 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -16,7 +16,7 @@ use schema; use rustc::dep_graph::DepGraph; use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, CrateNum, DefIndex, DefId}; -use rustc::hir::map::DefKey; +use rustc::hir::map::definitions::DefPathTable; use rustc::hir::svh::Svh; use rustc::middle::cstore::{DepKind, ExternCrate}; use rustc_back::PanicStrategy; @@ -78,7 +78,7 @@ pub struct CrateMetadata { /// hashmap, which gives the reverse mapping. This allows us to /// quickly retrace a `DefPath`, which is needed for incremental /// compilation support. - pub key_map: FxHashMap<DefKey, DefIndex>, + pub def_path_table: DefPathTable, pub dep_kind: Cell<DepKind>, pub source: CrateSource, diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 1a1bb1432ee..0ac3ffd5cb9 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -22,8 +22,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc::dep_graph::DepNode; -use rustc::hir::map as hir_map; -use rustc::hir::map::DefKey; +use rustc::hir::map::{DefKey, DefPath, DisambiguatedDefPathData}; use rustc::mir::Mir; use rustc::util::nodemap::{NodeSet, DefIdMap}; use rustc_back::PanicStrategy; @@ -189,8 +188,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { self.get_crate_data(def_id.krate).get_trait_of_item(def_id.index) } - fn associated_item<'a>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) - -> Option<ty::AssociatedItem> + fn associated_item(&self, def: DefId) -> Option<ty::AssociatedItem> { self.dep_graph.read(DepNode::MetaData(def)); self.get_crate_data(def.krate).get_associated_item(def.index) @@ -336,18 +334,20 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { self.get_crate_data(cnum).is_no_builtins() } - fn def_index_for_def_key(&self, - cnum: CrateNum, - def: DefKey) - -> Option<DefIndex> { + fn retrace_path(&self, + cnum: CrateNum, + path: &[DisambiguatedDefPathData]) + -> Option<DefId> { let cdata = self.get_crate_data(cnum); - cdata.key_map.get(&def).cloned() + cdata.def_path_table + .retrace_path(&path) + .map(|index| DefId { krate: cnum, index: index }) } /// Returns the `DefKey` for a given `DefId`. This indicates the /// parent `DefId` as well as some idea of what kind of data the /// `DefId` refers to. - fn def_key(&self, def: DefId) -> hir_map::DefKey { + fn def_key(&self, def: DefId) -> DefKey { // Note: loading the def-key (or def-path) for a def-id is not // a *read* of its metadata. This is because the def-id is // really just an interned shorthand for a def-path, which is the @@ -357,7 +357,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { self.get_crate_data(def.krate).def_key(def.index) } - fn relative_def_path(&self, def: DefId) -> Option<hir_map::DefPath> { + fn def_path(&self, def: DefId) -> DefPath { // See `Note` above in `def_key()` for why this read is // commented out: // @@ -418,8 +418,6 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { ident: ast::Ident::with_empty_ctxt(name), id: ast::DUMMY_NODE_ID, span: local_span, - imported_from: None, // FIXME - allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"), attrs: attrs, body: body, }) diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 54c195b1881..400a3ac0e3e 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -12,12 +12,9 @@ use astencode::decode_inlined_item; use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary}; -use index::Index; use schema::*; -use rustc::hir::map as hir_map; -use rustc::hir::map::{DefKey, DefPathData}; -use rustc::util::nodemap::FxHashMap; +use rustc::hir::map::{DefKey, DefPath, DefPathData}; use rustc::hir; use rustc::hir::intravisit::IdRange; @@ -44,7 +41,7 @@ use rustc_serialize::{Decodable, Decoder, SpecializedDecoder, opaque}; use syntax::attr; use syntax::ast::{self, NodeId}; use syntax::codemap; -use syntax_pos::{self, Span, BytePos, Pos}; +use syntax_pos::{self, Span, BytePos, Pos, DUMMY_SP}; pub struct DecodeContext<'a, 'tcx: 'a> { opaque: opaque::Decoder<'a>, @@ -456,14 +453,6 @@ impl<'a, 'tcx> MetadataBlob { Lazy::with_position(pos).decode(self) } - /// Go through each item in the metadata and create a map from that - /// item's def-key to the item's DefIndex. - pub fn load_key_map(&self, index: LazySeq<Index>) -> FxHashMap<DefKey, DefIndex> { - index.iter_enumerated(self.raw_bytes()) - .map(|(index, item)| (item.decode(self).def_key.decode(self), index)) - .collect() - } - pub fn list_crate_metadata(&self, out: &mut io::Write) -> io::Result<()> { write!(out, "=External Dependencies=\n")?; let root = self.get_root(); @@ -515,7 +504,12 @@ impl<'tcx> EntryKind<'tcx> { } impl<'a, 'tcx> CrateMetadata { + fn is_proc_macro(&self, id: DefIndex) -> bool { + self.proc_macros.is_some() && id != CRATE_DEF_INDEX + } + fn maybe_entry(&self, item_id: DefIndex) -> Option<Lazy<Entry<'tcx>>> { + assert!(!self.is_proc_macro(item_id)); self.root.index.lookup(self.blob.raw_bytes(), item_id) } @@ -538,9 +532,8 @@ impl<'a, 'tcx> CrateMetadata { } } - fn item_name(&self, item: &Entry<'tcx>) -> ast::Name { - item.def_key - .decode(self) + fn item_name(&self, item_index: DefIndex) -> ast::Name { + self.def_key(item_index) .disambiguated_data .data .get_opt_name() @@ -548,18 +541,17 @@ impl<'a, 'tcx> CrateMetadata { } pub fn get_def(&self, index: DefIndex) -> Option<Def> { - if self.proc_macros.is_some() { - Some(match index { - CRATE_DEF_INDEX => Def::Mod(self.local_def_id(index)), - _ => Def::Macro(self.local_def_id(index)), - }) - } else { - self.entry(index).kind.to_def(self.local_def_id(index)) + match self.is_proc_macro(index) { + true => Some(Def::Macro(self.local_def_id(index))), + false => self.entry(index).kind.to_def(self.local_def_id(index)), } } pub fn get_span(&self, index: DefIndex, sess: &Session) -> Span { - self.entry(index).span.decode((self, sess)) + match self.is_proc_macro(index) { + true => DUMMY_SP, + false => self.entry(index).span.decode((self, sess)), + } } pub fn get_trait_def(&self, @@ -574,7 +566,7 @@ impl<'a, 'tcx> CrateMetadata { ty::TraitDef::new(self.local_def_id(item_id), data.unsafety, data.paren_sugar, - self.def_path(item_id).unwrap().deterministic_hash(tcx)) + self.def_path(item_id).deterministic_hash(tcx)) } fn get_variant(&self, @@ -590,13 +582,13 @@ impl<'a, 'tcx> CrateMetadata { (ty::VariantDef { did: self.local_def_id(data.struct_ctor.unwrap_or(index)), - name: self.item_name(item), + name: self.item_name(index), fields: item.children.decode(self).map(|index| { let f = self.entry(index); ty::FieldDef { did: self.local_def_id(index), - name: self.item_name(&f), - vis: f.visibility + name: self.item_name(index), + vis: f.visibility.decode(self) } }).collect(), disr_val: ConstInt::Infer(data.disr), @@ -670,23 +662,23 @@ impl<'a, 'tcx> CrateMetadata { } pub fn get_stability(&self, id: DefIndex) -> Option<attr::Stability> { - match self.proc_macros { - Some(_) if id != CRATE_DEF_INDEX => None, - _ => self.entry(id).stability.map(|stab| stab.decode(self)), + match self.is_proc_macro(id) { + true => None, + false => self.entry(id).stability.map(|stab| stab.decode(self)), } } pub fn get_deprecation(&self, id: DefIndex) -> Option<attr::Deprecation> { - match self.proc_macros { - Some(_) if id != CRATE_DEF_INDEX => None, - _ => self.entry(id).deprecation.map(|depr| depr.decode(self)), + match self.is_proc_macro(id) { + true => None, + false => self.entry(id).deprecation.map(|depr| depr.decode(self)), } } pub fn get_visibility(&self, id: DefIndex) -> ty::Visibility { - match self.proc_macros { - Some(_) => ty::Visibility::Public, - _ => self.entry(id).visibility, + match self.is_proc_macro(id) { + true => ty::Visibility::Public, + false => self.entry(id).visibility.decode(self), } } @@ -767,7 +759,7 @@ impl<'a, 'tcx> CrateMetadata { if let Some(def) = self.get_def(child_index) { callback(def::Export { def: def, - name: self.item_name(&self.entry(child_index)), + name: self.item_name(child_index), }); } } @@ -779,7 +771,7 @@ impl<'a, 'tcx> CrateMetadata { _ => {} } - let def_key = child.def_key.decode(self); + let def_key = self.def_key(child_index); if let (Some(def), Some(name)) = (self.get_def(child_index), def_key.disambiguated_data.data.get_opt_name()) { callback(def::Export { @@ -832,18 +824,17 @@ impl<'a, 'tcx> CrateMetadata { id: DefIndex) -> Option<&'tcx InlinedItem> { debug!("Looking up item: {:?}", id); + if self.is_proc_macro(id) { return None; } let item_doc = self.entry(id); let item_did = self.local_def_id(id); - let parent_def_id = self.local_def_id(self.def_key(id).parent.unwrap()); - let mut parent_def_path = self.def_path(id).unwrap(); - parent_def_path.data.pop(); item_doc.ast.map(|ast| { let ast = ast.decode(self); - decode_inlined_item(self, tcx, parent_def_path, parent_def_id, ast, item_did) + decode_inlined_item(self, tcx, ast, item_did) }) } pub fn is_item_mir_available(&self, id: DefIndex) -> bool { + !self.is_proc_macro(id) && self.maybe_entry(id).and_then(|item| item.decode(self).mir).is_some() } @@ -874,13 +865,16 @@ impl<'a, 'tcx> CrateMetadata { tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefIndex) -> Option<Mir<'tcx>> { - self.entry(id).mir.map(|mir| mir.decode((self, tcx))) + match self.is_proc_macro(id) { + true => None, + false => self.entry(id).mir.map(|mir| mir.decode((self, tcx))), + } } pub fn get_associated_item(&self, id: DefIndex) -> Option<ty::AssociatedItem> { let item = self.entry(id); let parent_and_name = || { - let def_key = item.def_key.decode(self); + let def_key = self.def_key(id); (self.local_def_id(def_key.parent.unwrap()), def_key.disambiguated_data.data.get_opt_name().unwrap()) }; @@ -891,7 +885,7 @@ impl<'a, 'tcx> CrateMetadata { ty::AssociatedItem { name: name, kind: ty::AssociatedKind::Const, - vis: item.visibility, + vis: item.visibility.decode(self), defaultness: container.defaultness(), def_id: self.local_def_id(id), container: container.with_def_id(parent), @@ -904,7 +898,7 @@ impl<'a, 'tcx> CrateMetadata { ty::AssociatedItem { name: name, kind: ty::AssociatedKind::Method, - vis: item.visibility, + vis: item.visibility.decode(self), defaultness: data.container.defaultness(), def_id: self.local_def_id(id), container: data.container.with_def_id(parent), @@ -916,7 +910,7 @@ impl<'a, 'tcx> CrateMetadata { ty::AssociatedItem { name: name, kind: ty::AssociatedKind::Type, - vis: item.visibility, + vis: item.visibility.decode(self), defaultness: container.defaultness(), def_id: self.local_def_id(id), container: container.with_def_id(parent), @@ -950,14 +944,14 @@ impl<'a, 'tcx> CrateMetadata { } pub fn get_item_attrs(&self, node_id: DefIndex) -> Vec<ast::Attribute> { - if self.proc_macros.is_some() && node_id != CRATE_DEF_INDEX { + if self.is_proc_macro(node_id) { return Vec::new(); } // The attributes for a tuple struct are attached to the definition, not the ctor; // we assume that someone passing in a tuple struct ctor is actually wanting to // look at the definition let mut item = self.entry(node_id); - let def_key = item.def_key.decode(self); + let def_key = self.def_key(node_id); if def_key.disambiguated_data.data == DefPathData::StructCtor { item = self.entry(def_key.parent.unwrap()); } @@ -968,7 +962,7 @@ impl<'a, 'tcx> CrateMetadata { self.entry(id) .children .decode(self) - .map(|index| self.item_name(&self.entry(index))) + .map(|index| self.item_name(index)) .collect() } @@ -1012,6 +1006,7 @@ impl<'a, 'tcx> CrateMetadata { let filter = match filter.map(|def_id| self.reverse_translate_def_id(def_id)) { Some(Some(def_id)) => Some((def_id.krate.as_u32(), def_id.index)), Some(None) => return, + None if self.proc_macros.is_some() => return, None => None, }; @@ -1030,7 +1025,7 @@ impl<'a, 'tcx> CrateMetadata { } pub fn get_trait_of_item(&self, id: DefIndex) -> Option<DefId> { - self.entry(id).def_key.decode(self).parent.and_then(|parent_index| { + self.def_key(id).parent.and_then(|parent_index| { match self.entry(parent_index).kind { EntryKind::Trait(_) => Some(self.local_def_id(parent_index)), _ => None, @@ -1076,7 +1071,7 @@ impl<'a, 'tcx> CrateMetadata { pub fn get_macro(&self, id: DefIndex) -> (ast::Name, MacroDef) { let entry = self.entry(id); match entry.kind { - EntryKind::MacroDef(macro_def) => (self.item_name(&entry), macro_def.decode(self)), + EntryKind::MacroDef(macro_def) => (self.item_name(id), macro_def.decode(self)), _ => bug!(), } } @@ -1129,21 +1124,14 @@ impl<'a, 'tcx> CrateMetadata { } } - pub fn def_key(&self, id: DefIndex) -> hir_map::DefKey { - debug!("def_key: id={:?}", id); - self.entry(id).def_key.decode(self) + pub fn def_key(&self, index: DefIndex) -> DefKey { + self.def_path_table.def_key(index) } - // Returns the path leading to the thing with this `id`. Note that - // some def-ids don't wind up in the metadata, so `def_path` sometimes - // returns `None` - pub fn def_path(&self, id: DefIndex) -> Option<hir_map::DefPath> { + // Returns the path leading to the thing with this `id`. + pub fn def_path(&self, id: DefIndex) -> DefPath { debug!("def_path(id={:?})", id); - if self.maybe_entry(id).is_some() { - Some(hir_map::DefPath::make(self.cnum, id, |parent| self.def_key(parent))) - } else { - None - } + DefPath::make(self.cnum, id, |parent| self.def_path_table.def_key(parent)) } /// Imports the codemap from an external crate into the codemap of the crate diff --git a/src/librustc_metadata/diagnostics.rs b/src/librustc_metadata/diagnostics.rs index 6cf1a9e8a39..d3a2b6f1683 100644 --- a/src/librustc_metadata/diagnostics.rs +++ b/src/librustc_metadata/diagnostics.rs @@ -57,9 +57,9 @@ An unknown "kind" was specified for a link attribute. Erroneous code example: Please specify a valid "kind" value, from one of the following: - * static - * dylib - * framework +* static +* dylib +* framework "##, diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 443f3fbaa6e..bc0a64b9a51 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -16,6 +16,7 @@ use rustc::middle::cstore::{InlinedItemRef, LinkMeta}; use rustc::middle::cstore::{LinkagePreference, NativeLibrary}; use rustc::hir::def; use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefIndex, DefId}; +use rustc::hir::map::definitions::DefPathTable; use rustc::middle::dependency_format::Linkage; use rustc::middle::lang_items; use rustc::mir; @@ -233,13 +234,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Ok(()) } - /// For every DefId that we create a metadata item for, we include a - /// serialized copy of its DefKey, which allows us to recreate a path. - fn encode_def_key(&mut self, def_id: DefId) -> Lazy<hir::map::DefKey> { - let tcx = self.tcx; - self.lazy(&tcx.map.def_key(def_id)) - } - fn encode_item_variances(&mut self, def_id: DefId) -> LazySeq<ty::Variance> { let tcx = self.tcx; self.lazy_seq(tcx.item_variances(def_id).iter().cloned()) @@ -274,9 +268,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Entry { kind: EntryKind::Variant(self.lazy(&data)), - visibility: enum_vis.simplify(), + visibility: self.lazy(&ty::Visibility::from_hir(enum_vis, enum_id, tcx)), span: self.lazy(&tcx.def_span(def_id)), - def_key: self.encode_def_key(def_id), attributes: self.encode_attributes(&tcx.get_attrs(def_id)), children: self.lazy_seq(variant.fields.iter().map(|f| { assert!(f.did.is_local()); @@ -313,9 +306,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Entry { kind: EntryKind::Mod(self.lazy(&data)), - visibility: vis.simplify(), + visibility: self.lazy(&ty::Visibility::from_hir(vis, id, tcx)), span: self.lazy(&md.inner), - def_key: self.encode_def_key(def_id), attributes: self.encode_attributes(attrs), children: self.lazy_seq(md.item_ids.iter().map(|item_id| { tcx.map.local_def_id(item_id.id).index @@ -335,30 +327,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } -trait Visibility { - fn simplify(&self) -> ty::Visibility; -} - -impl Visibility for hir::Visibility { - fn simplify(&self) -> ty::Visibility { - if *self == hir::Public { - ty::Visibility::Public - } else { - ty::Visibility::PrivateExternal - } - } -} - -impl Visibility for ty::Visibility { - fn simplify(&self) -> ty::Visibility { - if *self == ty::Visibility::Public { - ty::Visibility::Public - } else { - ty::Visibility::PrivateExternal - } - } -} - impl<'a, 'b, 'tcx> IndexBuilder<'a, 'b, 'tcx> { fn encode_fields(&mut self, adt_def_id: DefId) { let def = self.tcx.lookup_adt_def(adt_def_id); @@ -394,9 +362,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Entry { kind: EntryKind::Field, - visibility: field.vis.simplify(), + visibility: self.lazy(&field.vis), span: self.lazy(&tcx.def_span(def_id)), - def_key: self.encode_def_key(def_id), attributes: self.encode_attributes(&variant_data.fields()[field_index].attrs), children: LazySeq::empty(), stability: self.encode_stability(def_id), @@ -428,9 +395,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Entry { kind: EntryKind::Struct(self.lazy(&data)), - visibility: struct_vis.simplify(), + visibility: self.lazy(&ty::Visibility::from_hir(struct_vis, struct_id, tcx)), span: self.lazy(&tcx.def_span(def_id)), - def_key: self.encode_def_key(def_id), attributes: LazySeq::empty(), children: LazySeq::empty(), stability: self.encode_stability(def_id), @@ -495,9 +461,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Entry { kind: kind, - visibility: trait_item.vis.simplify(), + visibility: self.lazy(&trait_item.vis), span: self.lazy(&ast_item.span), - def_key: self.encode_def_key(def_id), attributes: self.encode_attributes(&ast_item.attrs), children: LazySeq::empty(), stability: self.encode_stability(def_id), @@ -585,9 +550,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Entry { kind: kind, - visibility: impl_item.vis.simplify(), + visibility: self.lazy(&impl_item.vis), span: self.lazy(&ast_item.span), - def_key: self.encode_def_key(def_id), attributes: self.encode_attributes(&ast_item.attrs), children: LazySeq::empty(), stability: self.encode_stability(def_id), @@ -748,9 +712,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Entry { kind: kind, - visibility: item.vis.simplify(), + visibility: self.lazy(&ty::Visibility::from_hir(&item.vis, item.id, tcx)), span: self.lazy(&item.span), - def_key: self.encode_def_key(def_id), attributes: self.encode_attributes(&item.attrs), children: match item.node { hir::ItemForeignMod(ref fm) => { @@ -858,14 +821,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { /// Serialize the text of exported macros fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef) -> Entry<'tcx> { - let def_id = self.tcx.map.local_def_id(macro_def.id); Entry { kind: EntryKind::MacroDef(self.lazy(&MacroDef { body: ::syntax::print::pprust::tts_to_string(¯o_def.body) })), - visibility: ty::Visibility::Public, + visibility: self.lazy(&ty::Visibility::Public), span: self.lazy(¯o_def.span), - def_key: self.encode_def_key(def_id), attributes: self.encode_attributes(¯o_def.attrs), children: LazySeq::empty(), @@ -965,9 +926,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Entry { kind: kind, - visibility: nitem.vis.simplify(), + visibility: self.lazy(&ty::Visibility::from_hir(&nitem.vis, nitem.id, tcx)), span: self.lazy(&nitem.span), - def_key: self.encode_def_key(def_id), attributes: self.encode_attributes(&nitem.attrs), children: LazySeq::empty(), stability: self.encode_stability(def_id), @@ -1048,9 +1008,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; Entry { kind: EntryKind::Type, - visibility: ty::Visibility::Public, + visibility: self.lazy(&ty::Visibility::Public), span: self.lazy(&tcx.def_span(def_id)), - def_key: self.encode_def_key(def_id), attributes: LazySeq::empty(), children: LazySeq::empty(), stability: None, @@ -1077,9 +1036,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { Entry { kind: EntryKind::Closure(self.lazy(&data)), - visibility: ty::Visibility::Public, + visibility: self.lazy(&ty::Visibility::Public), span: self.lazy(&tcx.def_span(def_id)), - def_key: self.encode_def_key(def_id), attributes: self.encode_attributes(&tcx.get_attrs(def_id)), children: LazySeq::empty(), stability: None, @@ -1179,6 +1137,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }) .map(|filemap| &**filemap)) } + + fn encode_def_path_table(&mut self) -> Lazy<DefPathTable> { + let definitions = self.tcx.map.definitions(); + self.lazy(definitions.def_path_table()) + } } struct ImplVisitor<'a, 'tcx: 'a> { @@ -1276,6 +1239,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let codemap = self.encode_codemap(); let codemap_bytes = self.position() - i; + // Encode DefPathTable + i = self.position(); + let def_path_table = self.encode_def_path_table(); + let def_path_table_bytes = self.position() - i; + // Encode the def IDs of impls, for coherence checking. i = self.position(); let impls = self.encode_impls(); @@ -1321,6 +1289,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { lang_items_missing: lang_items_missing, native_libraries: native_libraries, codemap: codemap, + def_path_table: def_path_table, impls: impls, exported_symbols: exported_symbols, index: index, @@ -1343,6 +1312,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { println!(" codemap bytes: {}", codemap_bytes); println!(" impl bytes: {}", impl_bytes); println!(" exp. symbols bytes: {}", exported_symbols_bytes); + println!(" def-path table bytes: {}", def_path_table_bytes); println!(" item bytes: {}", item_bytes); println!(" index bytes: {}", index_bytes); println!(" zero bytes: {}", zero_bytes); diff --git a/src/librustc_metadata/index.rs b/src/librustc_metadata/index.rs index 53e6988c756..5b52b268849 100644 --- a/src/librustc_metadata/index.rs +++ b/src/librustc_metadata/index.rs @@ -70,7 +70,7 @@ impl<'tcx> LazySeq<Index> { index, words.len()); - let position = u32::from_le(words[index]); + let position = u32::from_le(words[index].get()); if position == u32::MAX { debug!("Index::lookup: position=u32::MAX"); None @@ -84,7 +84,7 @@ impl<'tcx> LazySeq<Index> { bytes: &'a [u8]) -> impl Iterator<Item = (DefIndex, Lazy<Entry<'tcx>>)> + 'a { let words = &bytes_to_words(&bytes[self.position..])[..self.len]; - words.iter().enumerate().filter_map(|(index, &position)| { + words.iter().map(|word| word.get()).enumerate().filter_map(|(index, position)| { if position == u32::MAX { None } else { @@ -95,8 +95,16 @@ impl<'tcx> LazySeq<Index> { } } -fn bytes_to_words(b: &[u8]) -> &[u32] { - unsafe { slice::from_raw_parts(b.as_ptr() as *const u32, b.len() / 4) } +#[repr(packed)] +#[derive(Copy, Clone)] +struct Unaligned<T>(T); + +impl<T> Unaligned<T> { + fn get(self) -> T { self.0 } +} + +fn bytes_to_words(b: &[u8]) -> &[Unaligned<u32>] { + unsafe { slice::from_raw_parts(b.as_ptr() as *const Unaligned<u32>, b.len() / 4) } } fn words_to_bytes(w: &[u32]) -> &[u8] { diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index f92051cbf19..2bd5a9ea59d 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -179,6 +179,7 @@ pub struct CrateRoot { pub lang_items_missing: LazySeq<lang_items::LangItem>, pub native_libraries: LazySeq<NativeLibrary>, pub codemap: LazySeq<syntax_pos::FileMap>, + pub def_path_table: Lazy<hir::map::definitions::DefPathTable>, pub impls: LazySeq<TraitImpls>, pub exported_symbols: LazySeq<DefIndex>, pub index: LazySeq<index::Index>, @@ -200,9 +201,8 @@ pub struct TraitImpls { #[derive(RustcEncodable, RustcDecodable)] pub struct Entry<'tcx> { pub kind: EntryKind<'tcx>, - pub visibility: ty::Visibility, + pub visibility: Lazy<ty::Visibility>, pub span: Lazy<Span>, - pub def_key: Lazy<hir::map::DefKey>, pub attributes: LazySeq<ast::Attribute>, pub children: LazySeq<DefIndex>, pub stability: Option<Lazy<attr::Stability>>, diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index cb69de2cb3c..b355c8f2c4c 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -26,7 +26,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block { extent: cx.tcx.region_maps.node_extent(self.id), span: self.span, stmts: stmts, - expr: self.expr.to_ref() + expr: self.expr.to_ref(), } } } @@ -34,39 +34,44 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block { fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, block_id: ast::NodeId, stmts: &'tcx [hir::Stmt]) - -> Vec<StmtRef<'tcx>> -{ + -> Vec<StmtRef<'tcx>> { let mut result = vec![]; for (index, stmt) in stmts.iter().enumerate() { match stmt.node { - hir::StmtExpr(ref expr, id) | hir::StmtSemi(ref expr, id) => + hir::StmtExpr(ref expr, id) | + hir::StmtSemi(ref expr, id) => { result.push(StmtRef::Mirror(Box::new(Stmt { span: stmt.span, kind: StmtKind::Expr { scope: cx.tcx.region_maps.node_extent(id), - expr: expr.to_ref() + expr: expr.to_ref(), + }, + }))) + } + hir::StmtDecl(ref decl, id) => { + match decl.node { + hir::DeclItem(..) => { + // ignore for purposes of the MIR } - }))), - hir::StmtDecl(ref decl, id) => match decl.node { - hir::DeclItem(..) => { /* ignore for purposes of the MIR */ } - hir::DeclLocal(ref local) => { - let remainder_extent = CodeExtentData::Remainder(BlockRemainder { - block: block_id, - first_statement_index: index as u32, - }); - let remainder_extent = - cx.tcx.region_maps.lookup_code_extent(remainder_extent); + hir::DeclLocal(ref local) => { + let remainder_extent = CodeExtentData::Remainder(BlockRemainder { + block: block_id, + first_statement_index: index as u32, + }); + let remainder_extent = + cx.tcx.region_maps.lookup_code_extent(remainder_extent); - let pattern = Pattern::from_hir(cx.tcx, &local.pat); - result.push(StmtRef::Mirror(Box::new(Stmt { - span: stmt.span, - kind: StmtKind::Let { - remainder_scope: remainder_extent, - init_scope: cx.tcx.region_maps.node_extent(id), - pattern: pattern, - initializer: local.init.to_ref(), - }, - }))); + let pattern = Pattern::from_hir(cx.tcx, &local.pat); + result.push(StmtRef::Mirror(Box::new(Stmt { + span: stmt.span, + kind: StmtKind::Let { + remainder_scope: remainder_extent, + init_scope: cx.tcx.region_maps.node_extent(id), + pattern: pattern, + initializer: local.init.to_ref(), + }, + }))); + } } } } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index bd4724159b4..d579cdb042f 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -36,7 +36,8 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { let adj = cx.tcx.tables().adjustments.get(&self.id).cloned(); debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}", - expr, adj); + expr, + adj); // Now apply adjustments, if any. match adj.map(|adj| (adj.kind, adj.target)) { @@ -78,41 +79,44 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { for i in 0..autoderefs { let i = i as u32; let adjusted_ty = - expr.ty.adjust_for_autoderef( - cx.tcx, - self.id, - self.span, - i, - |mc| cx.tcx.tables().method_map.get(&mc).map(|m| m.ty)); - debug!("make_mirror: autoderef #{}, adjusted_ty={:?}", i, adjusted_ty); + expr.ty.adjust_for_autoderef(cx.tcx, self.id, self.span, i, |mc| { + cx.tcx.tables().method_map.get(&mc).map(|m| m.ty) + }); + debug!("make_mirror: autoderef #{}, adjusted_ty={:?}", + i, + adjusted_ty); let method_key = ty::MethodCall::autoderef(self.id, i); - let meth_ty = - cx.tcx.tables().method_map.get(&method_key).map(|m| m.ty); + let meth_ty = cx.tcx.tables().method_map.get(&method_key).map(|m| m.ty); let kind = if let Some(meth_ty) = meth_ty { debug!("make_mirror: overloaded autoderef (meth_ty={:?})", meth_ty); let ref_ty = cx.tcx.no_late_bound_regions(&meth_ty.fn_ret()); let (region, mutbl) = match ref_ty { - Some(&ty::TyS { - sty: ty::TyRef(region, mt), .. - }) => (region, mt.mutbl), - _ => span_bug!(expr.span, "autoderef returned bad type") + Some(&ty::TyS { sty: ty::TyRef(region, mt), .. }) => (region, mt.mutbl), + _ => span_bug!(expr.span, "autoderef returned bad type"), }; expr = Expr { temp_lifetime: temp_lifetime, - ty: cx.tcx.mk_ref( - region, ty::TypeAndMut { ty: expr.ty, mutbl: mutbl }), + ty: cx.tcx.mk_ref(region, + ty::TypeAndMut { + ty: expr.ty, + mutbl: mutbl, + }), span: expr.span, kind: ExprKind::Borrow { region: region, borrow_kind: to_borrow_kind(mutbl), - arg: expr.to_ref() - } + arg: expr.to_ref(), + }, }; - overloaded_lvalue(cx, self, method_key, - PassArgs::ByRef, expr.to_ref(), vec![]) + overloaded_lvalue(cx, + self, + method_key, + PassArgs::ByRef, + expr.to_ref(), + vec![]) } else { debug!("make_mirror: built-in autoderef"); ExprKind::Deref { arg: expr.to_ref() } @@ -148,7 +152,11 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { let region = cx.tcx.mk_region(region); expr = Expr { temp_lifetime: temp_lifetime, - ty: cx.tcx.mk_ref(region, ty::TypeAndMut { ty: expr.ty, mutbl: m }), + ty: cx.tcx.mk_ref(region, + ty::TypeAndMut { + ty: expr.ty, + mutbl: m, + }), span: self.span, kind: ExprKind::Borrow { region: region, @@ -240,12 +248,12 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let sig = match method.ty.sty { ty::TyFnDef(.., fn_ty) => &fn_ty.sig, - _ => span_bug!(expr.span, "type of method is not an fn") + _ => span_bug!(expr.span, "type of method is not an fn"), }; - let sig = cx.tcx.no_late_bound_regions(sig).unwrap_or_else(|| { - span_bug!(expr.span, "method call has late-bound regions") - }); + let sig = cx.tcx + .no_late_bound_regions(sig) + .unwrap_or_else(|| span_bug!(expr.span, "method call has late-bound regions")); assert_eq!(sig.inputs().len(), 2); @@ -253,44 +261,49 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ty: sig.inputs()[1], temp_lifetime: temp_lifetime, span: expr.span, - kind: ExprKind::Tuple { - fields: args.iter().map(ToRef::to_ref).collect() - } + kind: ExprKind::Tuple { fields: args.iter().map(ToRef::to_ref).collect() }, }; ExprKind::Call { ty: method.ty, fun: method.to_ref(), - args: vec![fun.to_ref(), tupled_args.to_ref()] + args: vec![fun.to_ref(), tupled_args.to_ref()], } } else { let adt_data = if let hir::ExprPath(hir::QPath::Resolved(_, ref path)) = fun.node { // Tuple-like ADTs are represented as ExprCall. We convert them here. - expr_ty.ty_adt_def().and_then(|adt_def|{ + expr_ty.ty_adt_def().and_then(|adt_def| { match path.def { Def::VariantCtor(variant_id, CtorKind::Fn) => { Some((adt_def, adt_def.variant_index_with_id(variant_id))) - }, - Def::StructCtor(_, CtorKind::Fn) => { - Some((adt_def, 0)) - }, - _ => None + } + Def::StructCtor(_, CtorKind::Fn) => Some((adt_def, 0)), + _ => None, } }) - } else { None }; + } else { + None + }; if let Some((adt_def, index)) = adt_data { - let substs = cx.tcx.tables().node_id_item_substs(fun.id) + let substs = cx.tcx + .tables() + .node_id_item_substs(fun.id) .unwrap_or_else(|| cx.tcx.intern_substs(&[])); - let field_refs = args.iter().enumerate().map(|(idx, e)| FieldExprRef { - name: Field::new(idx), - expr: e.to_ref() - }).collect(); + let field_refs = args.iter() + .enumerate() + .map(|(idx, e)| { + FieldExprRef { + name: Field::new(idx), + expr: e.to_ref(), + } + }) + .collect(); ExprKind::Adt { adt_def: adt_def, substs: substs, variant_index: index, fields: field_refs, - base: None + base: None, } } else { ExprKind::Call { @@ -314,9 +327,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } - hir::ExprBlock(ref blk) => { - ExprKind::Block { body: &blk } - } + hir::ExprBlock(ref blk) => ExprKind::Block { body: &blk }, hir::ExprAssign(ref lhs, ref rhs) => { ExprKind::Assign { @@ -332,8 +343,12 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } else { PassArgs::ByRef }; - overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), - pass_args, lhs.to_ref(), vec![rhs]) + overloaded_operator(cx, + expr, + ty::MethodCall::expr(expr.id), + pass_args, + lhs.to_ref(), + vec![rhs]) } else { ExprKind::AssignOp { op: bin_op(op.node), @@ -343,9 +358,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } - hir::ExprLit(..) => ExprKind::Literal { - literal: cx.const_eval_literal(expr) - }, + hir::ExprLit(..) => ExprKind::Literal { literal: cx.const_eval_literal(expr) }, hir::ExprBinary(op, ref lhs, ref rhs) => { if cx.tcx.tables().is_method_call(expr.id) { @@ -354,8 +367,12 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } else { PassArgs::ByRef }; - overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), - pass_args, lhs.to_ref(), vec![rhs]) + overloaded_operator(cx, + expr, + ty::MethodCall::expr(expr.id), + pass_args, + lhs.to_ref(), + vec![rhs]) } else { // FIXME overflow match (op.node, cx.constness) { @@ -405,8 +422,12 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprIndex(ref lhs, ref index) => { if cx.tcx.tables().is_method_call(expr.id) { - overloaded_lvalue(cx, expr, ty::MethodCall::expr(expr.id), - PassArgs::ByValue, lhs.to_ref(), vec![index]) + overloaded_lvalue(cx, + expr, + ty::MethodCall::expr(expr.id), + PassArgs::ByValue, + lhs.to_ref(), + vec![index]) } else { ExprKind::Index { lhs: lhs.to_ref(), @@ -417,8 +438,12 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => { if cx.tcx.tables().is_method_call(expr.id) { - overloaded_lvalue(cx, expr, ty::MethodCall::expr(expr.id), - PassArgs::ByValue, arg.to_ref(), vec![]) + overloaded_lvalue(cx, + expr, + ty::MethodCall::expr(expr.id), + PassArgs::ByValue, + arg.to_ref(), + vec![]) } else { ExprKind::Deref { arg: arg.to_ref() } } @@ -426,8 +451,12 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprUnary(hir::UnOp::UnNot, ref arg) => { if cx.tcx.tables().is_method_call(expr.id) { - overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), - PassArgs::ByValue, arg.to_ref(), vec![]) + overloaded_operator(cx, + expr, + ty::MethodCall::expr(expr.id), + PassArgs::ByValue, + arg.to_ref(), + vec![]) } else { ExprKind::Unary { op: UnOp::Not, @@ -438,14 +467,16 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => { if cx.tcx.tables().is_method_call(expr.id) { - overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), - PassArgs::ByValue, arg.to_ref(), vec![]) + overloaded_operator(cx, + expr, + ty::MethodCall::expr(expr.id), + PassArgs::ByValue, + arg.to_ref(), + vec![]) } else { // FIXME runtime-overflow if let hir::ExprLit(_) = arg.node { - ExprKind::Literal { - literal: cx.const_eval_literal(expr), - } + ExprKind::Literal { literal: cx.const_eval_literal(expr) } } else { ExprKind::Unary { op: UnOp::Neg, @@ -457,56 +488,54 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprStruct(ref qpath, ref fields, ref base) => { match expr_ty.sty { - ty::TyAdt(adt, substs) => match adt.adt_kind() { - AdtKind::Struct | AdtKind::Union => { - let field_refs = field_refs(&adt.variants[0], fields); - ExprKind::Adt { - adt_def: adt, - variant_index: 0, - substs: substs, - fields: field_refs, - base: base.as_ref().map(|base| { - FruInfo { - base: base.to_ref(), - field_types: - cx.tcx.tables().fru_field_types[&expr.id].clone() - } - }) + ty::TyAdt(adt, substs) => { + match adt.adt_kind() { + AdtKind::Struct | AdtKind::Union => { + let field_refs = field_refs(&adt.variants[0], fields); + ExprKind::Adt { + adt_def: adt, + variant_index: 0, + substs: substs, + fields: field_refs, + base: base.as_ref().map(|base| { + FruInfo { + base: base.to_ref(), + field_types: cx.tcx.tables().fru_field_types[&expr.id] + .clone(), + } + }), + } } - } - AdtKind::Enum => { - let def = match *qpath { - hir::QPath::Resolved(_, ref path) => path.def, - hir::QPath::TypeRelative(..) => Def::Err - }; - match def { - Def::Variant(variant_id) => { - assert!(base.is_none()); - - let index = adt.variant_index_with_id(variant_id); - let field_refs = field_refs(&adt.variants[index], fields); - ExprKind::Adt { - adt_def: adt, - variant_index: index, - substs: substs, - fields: field_refs, - base: None + AdtKind::Enum => { + let def = match *qpath { + hir::QPath::Resolved(_, ref path) => path.def, + hir::QPath::TypeRelative(..) => Def::Err, + }; + match def { + Def::Variant(variant_id) => { + assert!(base.is_none()); + + let index = adt.variant_index_with_id(variant_id); + let field_refs = field_refs(&adt.variants[index], fields); + ExprKind::Adt { + adt_def: adt, + variant_index: index, + substs: substs, + fields: field_refs, + base: None, + } + } + _ => { + span_bug!(expr.span, "unexpected def: {:?}", def); } - } - _ => { - span_bug!( - expr.span, - "unexpected def: {:?}", - def); } } } - }, + } _ => { - span_bug!( - expr.span, - "unexpected type for struct literal: {:?}", - expr_ty); + span_bug!(expr.span, + "unexpected type for struct literal: {:?}", + expr_ty); } } } @@ -516,9 +545,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let (def_id, substs) = match closure_ty.sty { ty::TyClosure(def_id, substs) => (def_id, substs), _ => { - span_bug!(expr.span, - "closure expr w/o closure type: {:?}", - closure_ty); + span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty); } }; let upvars = cx.tcx.with_freevars(expr.id, |freevars| { @@ -543,69 +570,81 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ExprKind::InlineAsm { asm: asm, outputs: outputs.to_ref(), - inputs: inputs.to_ref() + inputs: inputs.to_ref(), } } // Now comes the rote stuff: - - hir::ExprRepeat(ref v, ref c) => ExprKind::Repeat { - value: v.to_ref(), - count: TypedConstVal { - ty: cx.tcx.tables().expr_ty(c), - span: c.span, - value: match const_eval::eval_const_expr(cx.tcx.global_tcx(), c) { - ConstVal::Integral(ConstInt::Usize(u)) => u, - other => bug!("constant evaluation of repeat count yielded {:?}", other), + hir::ExprRepeat(ref v, ref c) => { + ExprKind::Repeat { + value: v.to_ref(), + count: TypedConstVal { + ty: cx.tcx.tables().expr_ty(c), + span: c.span, + value: match const_eval::eval_const_expr(cx.tcx.global_tcx(), c) { + ConstVal::Integral(ConstInt::Usize(u)) => u, + other => bug!("constant evaluation of repeat count yielded {:?}", other), + }, }, } - }, - hir::ExprRet(ref v) => - ExprKind::Return { value: v.to_ref() }, - hir::ExprBreak(label, ref value) => + } + hir::ExprRet(ref v) => ExprKind::Return { value: v.to_ref() }, + hir::ExprBreak(label, ref value) => { ExprKind::Break { - label: label.map(|label| { - cx.tcx.region_maps.node_extent(label.loop_id) - }), - value: value.to_ref() - }, - hir::ExprAgain(label) => + label: label.map(|label| cx.tcx.region_maps.node_extent(label.loop_id)), + value: value.to_ref(), + } + } + hir::ExprAgain(label) => { ExprKind::Continue { - label: label.map(|label| { - cx.tcx.region_maps.node_extent(label.loop_id) - }) - }, - hir::ExprMatch(ref discr, ref arms, _) => - ExprKind::Match { discriminant: discr.to_ref(), - arms: arms.iter().map(|a| convert_arm(cx, a)).collect() }, - hir::ExprIf(ref cond, ref then, ref otherwise) => - ExprKind::If { condition: cond.to_ref(), - then: block::to_expr_ref(cx, then), - otherwise: otherwise.to_ref() }, - hir::ExprWhile(ref cond, ref body, _) => - ExprKind::Loop { condition: Some(cond.to_ref()), - body: block::to_expr_ref(cx, body) }, - hir::ExprLoop(ref body, _, _) => - ExprKind::Loop { condition: None, - body: block::to_expr_ref(cx, body) }, + label: label.map(|label| cx.tcx.region_maps.node_extent(label.loop_id)), + } + } + hir::ExprMatch(ref discr, ref arms, _) => { + ExprKind::Match { + discriminant: discr.to_ref(), + arms: arms.iter().map(|a| convert_arm(cx, a)).collect(), + } + } + hir::ExprIf(ref cond, ref then, ref otherwise) => { + ExprKind::If { + condition: cond.to_ref(), + then: block::to_expr_ref(cx, then), + otherwise: otherwise.to_ref(), + } + } + hir::ExprWhile(ref cond, ref body, _) => { + ExprKind::Loop { + condition: Some(cond.to_ref()), + body: block::to_expr_ref(cx, body), + } + } + hir::ExprLoop(ref body, _, _) => { + ExprKind::Loop { + condition: None, + body: block::to_expr_ref(cx, body), + } + } hir::ExprField(ref source, name) => { let index = match cx.tcx.tables().expr_ty_adjusted(source).sty { - ty::TyAdt(adt_def, _) => - adt_def.variants[0].index_of_field_named(name.node), - ref ty => - span_bug!(expr.span, "field of non-ADT: {:?}", ty), + ty::TyAdt(adt_def, _) => adt_def.variants[0].index_of_field_named(name.node), + ref ty => span_bug!(expr.span, "field of non-ADT: {:?}", ty), }; - let index = index.unwrap_or_else(|| { - span_bug!( - expr.span, - "no index found for field `{}`", - name.node) - }); - ExprKind::Field { lhs: source.to_ref(), name: Field::new(index) } + let index = + index.unwrap_or_else(|| { + span_bug!(expr.span, "no index found for field `{}`", name.node) + }); + ExprKind::Field { + lhs: source.to_ref(), + name: Field::new(index), + } + } + hir::ExprTupField(ref source, index) => { + ExprKind::Field { + lhs: source.to_ref(), + name: Field::new(index.node as usize), + } } - hir::ExprTupField(ref source, index) => - ExprKind::Field { lhs: source.to_ref(), - name: Field::new(index.node as usize) }, 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). @@ -616,17 +655,15 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ExprKind::Cast { source: source.to_ref() } } } - hir::ExprType(ref source, _) => - return source.make_mirror(cx), - hir::ExprBox(ref value) => + hir::ExprType(ref source, _) => return source.make_mirror(cx), + hir::ExprBox(ref value) => { ExprKind::Box { value: value.to_ref(), - value_extents: cx.tcx.region_maps.node_extent(value.id) - }, - hir::ExprArray(ref fields) => - ExprKind::Vec { fields: fields.to_ref() }, - hir::ExprTup(ref fields) => - ExprKind::Tuple { fields: fields.to_ref() }, + value_extents: cx.tcx.region_maps.node_extent(value.id), + } + } + hir::ExprArray(ref fields) => ExprKind::Vec { fields: fields.to_ref() }, + hir::ExprTup(ref fields) => ExprKind::Tuple { fields: fields.to_ref() }, }; Expr { @@ -663,8 +700,7 @@ fn to_borrow_kind(m: hir::Mutability) -> BorrowKind { } } -fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, - arm: &'tcx hir::Arm) -> Arm<'tcx> { +fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm) -> Arm<'tcx> { Arm { patterns: arm.pats.iter().map(|p| Pattern::from_hir(cx.tcx, p)).collect(), guard: arm.guard.to_ref(), @@ -676,41 +712,48 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, expr: &'tcx hir::Expr, def: Def) -> ExprKind<'tcx> { - let substs = cx.tcx.tables().node_id_item_substs(expr.id) + let substs = cx.tcx + .tables() + .node_id_item_substs(expr.id) .unwrap_or_else(|| cx.tcx.intern_substs(&[])); let def_id = match def { // A regular function, constructor function or a constant. - Def::Fn(def_id) | Def::Method(def_id) | + Def::Fn(def_id) | + Def::Method(def_id) | Def::StructCtor(def_id, CtorKind::Fn) | Def::VariantCtor(def_id, CtorKind::Fn) | - Def::Const(def_id) | Def::AssociatedConst(def_id) => def_id, + Def::Const(def_id) | + Def::AssociatedConst(def_id) => def_id, Def::StructCtor(def_id, CtorKind::Const) | Def::VariantCtor(def_id, CtorKind::Const) => { match cx.tcx.tables().node_id_to_type(expr.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) => return ExprKind::Adt { - adt_def: adt_def, - variant_index: adt_def.variant_index_with_id(def_id), - substs: substs, - fields: vec![], - base: None, - }, - ref sty => bug!("unexpected sty: {:?}", sty) + ty::TyAdt(adt_def, substs) => { + return ExprKind::Adt { + adt_def: adt_def, + variant_index: adt_def.variant_index_with_id(def_id), + substs: substs, + fields: vec![], + base: None, + } + } + ref sty => bug!("unexpected sty: {:?}", sty), } } - Def::Static(node_id, _) => return ExprKind::StaticRef { - id: node_id, - }, + Def::Static(node_id, _) => return ExprKind::StaticRef { id: node_id }, Def::Local(..) | Def::Upvar(..) => return convert_var(cx, expr, def), _ => span_bug!(expr.span, "def `{:?}` not yet implemented", def), }; ExprKind::Literal { - literal: Literal::Item { def_id: def_id, substs: substs } + literal: Literal::Item { + def_id: def_id, + substs: substs, + }, } } @@ -723,14 +766,15 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, match def { Def::Local(def_id) => { let node_id = cx.tcx.map.as_local_node_id(def_id).unwrap(); - ExprKind::VarRef { - id: node_id, - } + ExprKind::VarRef { id: node_id } } Def::Upvar(def_id, index, closure_expr_id) => { let id_var = cx.tcx.map.as_local_node_id(def_id).unwrap(); - debug!("convert_var(upvar({:?}, {:?}, {:?}))", id_var, index, closure_expr_id); + debug!("convert_var(upvar({:?}, {:?}, {:?}))", + id_var, + index, + closure_expr_id); let var_ty = cx.tcx.tables().node_id_to_type(id_var); let body_id = match cx.tcx.map.find(closure_expr_id) { @@ -761,41 +805,45 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let self_expr = match cx.tcx.closure_kind(cx.tcx.map.local_def_id(closure_expr_id)) { ty::ClosureKind::Fn => { - let ref_closure_ty = - cx.tcx.mk_ref(region, - ty::TypeAndMut { ty: closure_ty, - mutbl: hir::MutImmutable }); + let ref_closure_ty = cx.tcx.mk_ref(region, + ty::TypeAndMut { + ty: closure_ty, + mutbl: hir::MutImmutable, + }); Expr { ty: closure_ty, temp_lifetime: temp_lifetime, span: expr.span, kind: ExprKind::Deref { arg: Expr { - ty: ref_closure_ty, - temp_lifetime: temp_lifetime, - span: expr.span, - kind: ExprKind::SelfRef - }.to_ref() - } + ty: ref_closure_ty, + temp_lifetime: temp_lifetime, + span: expr.span, + kind: ExprKind::SelfRef, + } + .to_ref(), + }, } } ty::ClosureKind::FnMut => { - let ref_closure_ty = - cx.tcx.mk_ref(region, - ty::TypeAndMut { ty: closure_ty, - mutbl: hir::MutMutable }); + let ref_closure_ty = cx.tcx.mk_ref(region, + ty::TypeAndMut { + ty: closure_ty, + mutbl: hir::MutMutable, + }); Expr { ty: closure_ty, temp_lifetime: temp_lifetime, span: expr.span, kind: ExprKind::Deref { arg: Expr { - ty: ref_closure_ty, - temp_lifetime: temp_lifetime, - span: expr.span, - kind: ExprKind::SelfRef - }.to_ref() - } + ty: ref_closure_ty, + temp_lifetime: temp_lifetime, + span: expr.span, + kind: ExprKind::SelfRef, + } + .to_ref(), + }, } } ty::ClosureKind::FnOnce => { @@ -823,10 +871,7 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let upvar_capture = match cx.tcx.tables().upvar_capture(upvar_id) { Some(c) => c, None => { - span_bug!( - expr.span, - "no upvar_capture for {:?}", - upvar_id); + span_bug!(expr.span, "no upvar_capture for {:?}", upvar_id); } }; match upvar_capture { @@ -834,15 +879,16 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ty::UpvarCapture::ByRef(borrow) => { ExprKind::Deref { arg: Expr { - temp_lifetime: temp_lifetime, - ty: cx.tcx.mk_ref(borrow.region, - ty::TypeAndMut { - ty: var_ty, - mutbl: borrow.kind.to_mutbl_lossy() - }), - span: expr.span, - kind: field_kind, - }.to_ref() + temp_lifetime: temp_lifetime, + ty: cx.tcx.mk_ref(borrow.region, + ty::TypeAndMut { + ty: var_ty, + mutbl: borrow.kind.to_mutbl_lossy(), + }), + span: expr.span, + kind: field_kind, + } + .to_ref(), } } } @@ -894,30 +940,31 @@ fn overloaded_operator<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, // the arguments, unfortunately, do not, so if this is a ByRef // operator, we have to gin up the autorefs (but by value is easy) match pass_args { - PassArgs::ByValue => { - argrefs.extend(args.iter().map(|arg| arg.to_ref())) - } + PassArgs::ByValue => argrefs.extend(args.iter().map(|arg| arg.to_ref())), PassArgs::ByRef => { let region = cx.tcx.node_scope_region(expr.id); let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id); - argrefs.extend( - args.iter() - .map(|arg| { - let arg_ty = cx.tcx.tables().expr_ty_adjusted(arg); - let adjusted_ty = - cx.tcx.mk_ref(region, - ty::TypeAndMut { ty: arg_ty, - mutbl: hir::MutImmutable }); - Expr { + argrefs.extend(args.iter() + .map(|arg| { + let arg_ty = cx.tcx.tables().expr_ty_adjusted(arg); + let adjusted_ty = cx.tcx.mk_ref(region, + ty::TypeAndMut { + ty: arg_ty, + mutbl: hir::MutImmutable, + }); + Expr { temp_lifetime: temp_lifetime, ty: adjusted_ty, span: expr.span, - kind: ExprKind::Borrow { region: region, - borrow_kind: BorrowKind::Shared, - arg: arg.to_ref() } - }.to_ref() - })) + kind: ExprKind::Borrow { + region: region, + borrow_kind: BorrowKind::Shared, + arg: arg.to_ref(), + }, + } + .to_ref() + })) } } @@ -981,9 +1028,7 @@ fn capture_freevar<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, kind: convert_var(cx, closure_expr, freevar.def), }; match upvar_capture { - ty::UpvarCapture::ByValue => { - captured_var.to_ref() - } + ty::UpvarCapture::ByValue => captured_var.to_ref(), ty::UpvarCapture::ByRef(upvar_borrow) => { let borrow_kind = match upvar_borrow.kind { ty::BorrowKind::ImmBorrow => BorrowKind::Shared, @@ -991,13 +1036,16 @@ fn capture_freevar<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ty::BorrowKind::MutBorrow => BorrowKind::Mut, }; Expr { - temp_lifetime: temp_lifetime, - ty: freevar_ty, - span: closure_expr.span, - kind: ExprKind::Borrow { region: upvar_borrow.region, - borrow_kind: borrow_kind, - arg: captured_var.to_ref() } - }.to_ref() + temp_lifetime: temp_lifetime, + ty: freevar_ty, + span: closure_expr.span, + kind: ExprKind::Borrow { + region: upvar_borrow.region, + borrow_kind: borrow_kind, + arg: captured_var.to_ref(), + }, + } + .to_ref() } } } @@ -1005,12 +1053,13 @@ fn capture_freevar<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, /// Converts a list of named fields (i.e. for struct-like struct/enum ADTs) into FieldExprRef. fn field_refs<'tcx>(variant: &'tcx VariantDef, fields: &'tcx [hir::Field]) - -> Vec<FieldExprRef<'tcx>> -{ + -> Vec<FieldExprRef<'tcx>> { fields.iter() - .map(|field| FieldExprRef { - name: Field::new(variant.index_of_field_named(field.name.node).unwrap()), - expr: field.expr.to_ref(), - }) - .collect() + .map(|field| { + FieldExprRef { + name: Field::new(variant.index_of_field_named(field.name.node).unwrap()), + expr: field.expr.to_ref(), + } + }) + .collect() } diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index e7a6b40c830..7d111fccd00 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -8,12 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - * This module contains the code to convert from the wacky tcx data - * structures into the hair. The `builder` is generally ignorant of - * the tcx etc, and instead goes through the `Cx` for most of its - * work. - */ +//! This module contains the code to convert from the wacky tcx data +//! structures into the hair. The `builder` is generally ignorant of +//! the tcx etc, and instead goes through the `Cx` for most of its +//! work. +//! use hair::*; use rustc::mir::transform::MirSource; @@ -32,19 +31,17 @@ use rustc::hir; use rustc_const_math::{ConstInt, ConstUsize}; #[derive(Copy, Clone)] -pub struct Cx<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { tcx: TyCtxt<'a, 'gcx, 'tcx>, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, constness: hir::Constness, /// True if this constant/function needs overflow checks. - check_overflow: bool + check_overflow: bool, } impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - src: MirSource) - -> Cx<'a, 'gcx, 'tcx> { + pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, src: MirSource) -> Cx<'a, 'gcx, 'tcx> { let constness = match src { MirSource::Const(_) | MirSource::Static(..) => hir::Constness::Const, @@ -52,7 +49,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { let fn_like = FnLikeNode::from_node(infcx.tcx.map.get(id)); fn_like.map_or(hir::Constness::NotConst, |f| f.constness()) } - MirSource::Promoted(..) => bug!() + MirSource::Promoted(..) => bug!(), }; let src_node_id = src.item_id(); @@ -70,13 +67,16 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { // Some functions always have overflow checks enabled, // however, they may not get codegen'd, depending on // the settings for the crate they are translated in. - let mut check_overflow = attrs.iter().any(|item| { - item.check_name("rustc_inherit_overflow_checks") - }); + let mut check_overflow = attrs.iter() + .any(|item| item.check_name("rustc_inherit_overflow_checks")); // Respect -Z force-overflow-checks=on and -C debug-assertions. - check_overflow |= infcx.tcx.sess.opts.debugging_opts.force_overflow_checks - .unwrap_or(infcx.tcx.sess.opts.debug_assertions); + check_overflow |= infcx.tcx + .sess + .opts + .debugging_opts + .force_overflow_checks + .unwrap_or(infcx.tcx.sess.opts.debug_assertions); // Constants and const fn's always need overflow checks. check_overflow |= constness == hir::Constness::Const; @@ -85,7 +85,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { tcx: infcx.tcx, infcx: infcx, constness: constness, - check_overflow: check_overflow + check_overflow: check_overflow, } } } @@ -102,7 +102,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn usize_literal(&mut self, value: u64) -> Literal<'tcx> { match ConstUsize::new(value, self.tcx.sess.target.uint_type) { - Ok(val) => Literal::Value { value: ConstVal::Integral(ConstInt::Usize(val))}, + Ok(val) => Literal::Value { value: ConstVal::Integral(ConstInt::Usize(val)) }, Err(_) => bug!("usize literal out of range for target"), } } @@ -128,9 +128,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { } pub fn const_eval_literal(&mut self, e: &hir::Expr) -> Literal<'tcx> { - Literal::Value { - value: const_eval::eval_const_expr(self.tcx.global_tcx(), e) - } + Literal::Value { value: const_eval::eval_const_expr(self.tcx.global_tcx(), e) } } pub fn trait_method(&mut self, @@ -145,10 +143,11 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { if item.kind == ty::AssociatedKind::Method && item.name == method_name { let method_ty = self.tcx.item_type(item.def_id); let method_ty = method_ty.subst(self.tcx, substs); - return (method_ty, Literal::Item { - def_id: item.def_id, - substs: substs, - }); + return (method_ty, + Literal::Item { + def_id: item.def_id, + substs: substs, + }); } } @@ -168,7 +167,8 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn needs_drop(&mut self, ty: Ty<'tcx>) -> bool { let ty = self.tcx.lift_to_global(&ty).unwrap_or_else(|| { bug!("MIR: Cx::needs_drop({}) got \ - type with inference types/regions", ty); + type with inference types/regions", + ty); }); self.tcx.type_needs_drop_given_env(ty, &self.infcx.parameter_environment) } diff --git a/src/librustc_mir/hair/cx/to_ref.rs b/src/librustc_mir/hair/cx/to_ref.rs index 63dbde47438..6930a959d65 100644 --- a/src/librustc_mir/hair/cx/to_ref.rs +++ b/src/librustc_mir/hair/cx/to_ref.rs @@ -18,7 +18,7 @@ pub trait ToRef { fn to_ref(self) -> Self::Output; } -impl<'a,'tcx:'a> ToRef for &'tcx hir::Expr { +impl<'a, 'tcx: 'a> ToRef for &'tcx hir::Expr { type Output = ExprRef<'tcx>; fn to_ref(self) -> ExprRef<'tcx> { @@ -26,7 +26,7 @@ impl<'a,'tcx:'a> ToRef for &'tcx hir::Expr { } } -impl<'a,'tcx:'a> ToRef for &'tcx P<hir::Expr> { +impl<'a, 'tcx: 'a> ToRef for &'tcx P<hir::Expr> { type Output = ExprRef<'tcx>; fn to_ref(self) -> ExprRef<'tcx> { @@ -34,7 +34,7 @@ impl<'a,'tcx:'a> ToRef for &'tcx P<hir::Expr> { } } -impl<'a,'tcx:'a> ToRef for Expr<'tcx> { +impl<'a, 'tcx: 'a> ToRef for Expr<'tcx> { type Output = ExprRef<'tcx>; fn to_ref(self) -> ExprRef<'tcx> { @@ -42,8 +42,8 @@ impl<'a,'tcx:'a> ToRef for Expr<'tcx> { } } -impl<'a,'tcx:'a,T,U> ToRef for &'tcx Option<T> - where &'tcx T: ToRef<Output=U> +impl<'a, 'tcx: 'a, T, U> ToRef for &'tcx Option<T> + where &'tcx T: ToRef<Output = U> { type Output = Option<U>; @@ -52,8 +52,8 @@ impl<'a,'tcx:'a,T,U> ToRef for &'tcx Option<T> } } -impl<'a,'tcx:'a,T,U> ToRef for &'tcx Vec<T> - where &'tcx T: ToRef<Output=U> +impl<'a, 'tcx: 'a, T, U> ToRef for &'tcx Vec<T> + where &'tcx T: ToRef<Output = U> { type Output = Vec<U>; @@ -62,8 +62,8 @@ impl<'a,'tcx:'a,T,U> ToRef for &'tcx Vec<T> } } -impl<'a,'tcx:'a,T,U> ToRef for &'tcx P<[T]> - where &'tcx T: ToRef<Output=U> +impl<'a, 'tcx: 'a, T, U> ToRef for &'tcx P<[T]> + where &'tcx T: ToRef<Output = U> { type Output = Vec<U>; diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs index d16b51adbaf..3cbf8573ba9 100644 --- a/src/librustc_mir/transform/copy_prop.rs +++ b/src/librustc_mir/transform/copy_prop.rs @@ -30,7 +30,7 @@ //! future. use def_use::DefUseAnalysis; -use rustc::mir::{Constant, Local, Location, Lvalue, Mir, Operand, Rvalue, StatementKind}; +use rustc::mir::{Constant, Local, LocalKind, Location, Lvalue, Mir, Operand, Rvalue, StatementKind}; use rustc::mir::transform::{MirPass, MirSource, Pass}; use rustc::mir::visit::MutVisitor; use rustc::ty::TyCtxt; @@ -122,7 +122,7 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation { local == dest_local => { let maybe_action = match *operand { Operand::Consume(ref src_lvalue) => { - Action::local_copy(&def_use_analysis, src_lvalue) + Action::local_copy(&mir, &def_use_analysis, src_lvalue) } Operand::Constant(ref src_constant) => { Action::constant(src_constant) @@ -159,7 +159,7 @@ enum Action<'tcx> { } impl<'tcx> Action<'tcx> { - fn local_copy(def_use_analysis: &DefUseAnalysis, src_lvalue: &Lvalue<'tcx>) + fn local_copy(mir: &Mir<'tcx>, def_use_analysis: &DefUseAnalysis, src_lvalue: &Lvalue<'tcx>) -> Option<Action<'tcx>> { // The source must be a local. let src_local = if let Lvalue::Local(local) = *src_lvalue { @@ -195,7 +195,9 @@ impl<'tcx> Action<'tcx> { // SRC = X; // USE(SRC); let src_def_count = src_use_info.def_count_not_including_drop(); - if src_def_count != 1 { + // allow function arguments to be propagated + if src_def_count > 1 || + (src_def_count == 0 && mir.local_kind(src_local) != LocalKind::Arg) { debug!(" Can't copy-propagate local: {} defs of src", src_use_info.def_count_not_including_drop()); return None diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 2d0f0864752..52bdd014933 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -154,8 +154,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_path(&mut self, path: &'a Path, id: NodeId) { - if path.global && path.segments.len() > 0 { - let ident = path.segments[0].identifier; + if path.segments.len() >= 2 && path.is_global() { + let ident = path.segments[1].identifier; if token::Ident(ident).is_path_segment_keyword() { self.session.add_lint(lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH, id, @@ -171,7 +171,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { match item.node { ItemKind::Use(ref view_path) => { let path = view_path.node.path(); - if !path.segments.iter().all(|segment| segment.parameters.is_empty()) { + if path.segments.iter().any(|segment| segment.parameters.is_some()) { self.err_handler() .span_err(path.span, "type or lifetime parameters in import path"); } @@ -275,7 +275,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_vis(&mut self, vis: &'a Visibility) { match *vis { Visibility::Restricted { ref path, .. } => { - if !path.segments.iter().all(|segment| segment.parameters.is_empty()) { + if !path.segments.iter().all(|segment| segment.parameters.is_none()) { self.err_handler() .span_err(path.span, "type or lifetime parameters in visibility path"); } diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index fe2f9713d1b..3700d0295e9 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -64,6 +64,8 @@ pub struct Registry<'a> { #[doc(hidden)] pub attributes: Vec<(String, AttributeType)>, + + whitelisted_custom_derives: Vec<ast::Name>, } impl<'a> Registry<'a> { @@ -80,6 +82,7 @@ impl<'a> Registry<'a> { llvm_passes: vec![], attributes: vec![], mir_passes: Vec::new(), + whitelisted_custom_derives: Vec::new(), } } @@ -115,6 +118,21 @@ impl<'a> Registry<'a> { })); } + /// This can be used in place of `register_syntax_extension` to register legacy custom derives + /// (i.e. attribute syntax extensions whose name begins with `derive_`). Legacy custom + /// derives defined by this function do not trigger deprecation warnings when used. + #[unstable(feature = "rustc_private", issue = "27812")] + #[rustc_deprecated(since = "1.15.0", reason = "replaced by macros 1.1 (RFC 1861)")] + pub fn register_custom_derive(&mut self, name: ast::Name, extension: SyntaxExtension) { + assert!(name.as_str().starts_with("derive_")); + self.whitelisted_custom_derives.push(name); + self.register_syntax_extension(name, extension); + } + + pub fn take_whitelisted_custom_derives(&mut self) -> Vec<ast::Name> { + ::std::mem::replace(&mut self.whitelisted_custom_derives, Vec::new()) + } + /// Register a macro of the usual kind. /// /// This is a convenience wrapper for `register_syntax_extension`. diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 145b9176f6b..ca3e4e1c762 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -28,7 +28,7 @@ extern crate syntax_pos; use rustc::dep_graph::DepNode; use rustc::hir::{self, PatKind}; use rustc::hir::def::{self, Def, CtorKind}; -use rustc::hir::def_id::DefId; +use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::itemlikevisit::DeepVisitor; use rustc::hir::pat_util::EnumerateAndAdjustIterator; @@ -391,7 +391,7 @@ impl<'b, 'a, 'tcx> TypeVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b struct PrivacyVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, - curitem: ast::NodeId, + curitem: DefId, in_foreign: bool, } @@ -401,12 +401,12 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { Some(node_id) => ty::Visibility::from_hir(&self.tcx.map.expect_item(node_id).vis, node_id, self.tcx), None => self.tcx.sess.cstore.visibility(did), - }.is_accessible_from(self.curitem, &self.tcx.map) + }.is_accessible_from(self.curitem, self.tcx) } // Checks that a field is in scope. fn check_field(&mut self, span: Span, def: &'tcx ty::AdtDef, field: &'tcx ty::FieldDef) { - if !def.is_enum() && !field.vis.is_accessible_from(self.curitem, &self.tcx.map) { + if !def.is_enum() && !field.vis.is_accessible_from(self.curitem, self.tcx) { struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private", field.name, def.variant_descr(), self.tcx.item_path_str(def.did)) .span_label(span, &format!("field `{}` is private", field.name)) @@ -437,7 +437,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivacyVisitor<'a, 'tcx> { } fn visit_item(&mut self, item: &'tcx hir::Item) { - let orig_curitem = replace(&mut self.curitem, item.id); + let orig_curitem = replace(&mut self.curitem, self.tcx.map.local_def_id(item.id)); intravisit::walk_item(self, item); self.curitem = orig_curitem; } @@ -474,7 +474,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivacyVisitor<'a, 'tcx> { if let Def::StructCtor(_, CtorKind::Fn) = path.def { let adt_def = self.tcx.expect_variant_def(path.def); let private_indexes = adt_def.fields.iter().enumerate().filter(|&(_, field)| { - !field.vis.is_accessible_from(self.curitem, &self.tcx.map) + !field.vis.is_accessible_from(self.curitem, self.tcx) }).map(|(i, _)| i).collect::<Vec<_>>(); if !private_indexes.is_empty() { @@ -940,7 +940,7 @@ impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<' ty::TyAdt(adt, _) => Some(adt.did), ty::TyDynamic(ref obj, ..) => obj.principal().map(|p| p.def_id()), ty::TyProjection(ref proj) => { - if self.required_visibility == ty::Visibility::PrivateExternal { + if self.required_visibility == ty::Visibility::Invisible { // Conservatively approximate the whole type alias as public without // recursing into its components when determining impl publicity. // For example, `impl <Type as Trait>::Alias {...}` may be a public impl @@ -961,10 +961,10 @@ impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<' let item = self.tcx.map.expect_item(node_id); let vis = ty::Visibility::from_hir(&item.vis, node_id, self.tcx); - if !vis.is_at_least(self.min_visibility, &self.tcx.map) { + if !vis.is_at_least(self.min_visibility, self.tcx) { self.min_visibility = vis; } - if !vis.is_at_least(self.required_visibility, &self.tcx.map) { + if !vis.is_at_least(self.required_visibility, self.tcx) { if self.tcx.sess.features.borrow().pub_restricted || self.has_old_errors { let mut err = struct_span_err!(self.tcx.sess, self.span, E0446, "private type `{}` in public interface", ty); @@ -996,10 +996,10 @@ impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<' let item = self.tcx.map.expect_item(node_id); let vis = ty::Visibility::from_hir(&item.vis, node_id, self.tcx); - if !vis.is_at_least(self.min_visibility, &self.tcx.map) { + if !vis.is_at_least(self.min_visibility, self.tcx) { self.min_visibility = vis; } - if !vis.is_at_least(self.required_visibility, &self.tcx.map) { + if !vis.is_at_least(self.required_visibility, self.tcx) { if self.tcx.sess.features.borrow().pub_restricted || self.has_old_errors { struct_span_err!(self.tcx.sess, self.span, E0445, "private trait `{}` in public interface", trait_ref) @@ -1071,7 +1071,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> fn visit_item(&mut self, item: &'tcx hir::Item) { let tcx = self.tcx; let min = |vis1: ty::Visibility, vis2| { - if vis1.is_at_least(vis2, &tcx.map) { vis2 } else { vis1 } + if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 } }; let item_visibility = ty::Visibility::from_hir(&item.vis, item.id, tcx); @@ -1137,8 +1137,8 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> // An inherent impl is public when its type is public // Subitems of inherent impls have their own publicity hir::ItemImpl(.., None, _, ref impl_item_refs) => { - let ty_vis = self.check(item.id, ty::Visibility::PrivateExternal) - .item_type().min_visibility; + let ty_vis = + self.check(item.id, ty::Visibility::Invisible).item_type().min_visibility; self.check(item.id, ty_vis).generics().predicates(); for impl_item_ref in impl_item_refs { @@ -1156,7 +1156,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> // A trait impl is public when both its type and its trait are public // Subitems of trait impls have inherited publicity hir::ItemImpl(.., Some(_), _, ref impl_item_refs) => { - let vis = self.check(item.id, ty::Visibility::PrivateExternal) + let vis = self.check(item.id, ty::Visibility::Invisible) .item_type().impl_trait_ref().min_visibility; self.check(item.id, vis).generics().predicates(); for impl_item_ref in impl_item_refs { @@ -1203,7 +1203,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Use the parent map to check the privacy of everything let mut visitor = PrivacyVisitor { - curitem: ast::DUMMY_NODE_ID, + curitem: DefId::local(CRATE_DEF_INDEX), in_foreign: false, tcx: tcx, }; diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 25a37931ba3..1b3791355d2 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -16,7 +16,8 @@ use macros::{InvocationData, LegacyScope}; use resolve_imports::ImportDirective; use resolve_imports::ImportDirectiveSubclass::{self, GlobImport, SingleImport}; -use {Resolver, Module, ModuleS, ModuleKind, NameBinding, NameBindingKind, ToNameBinding}; +use {Module, ModuleData, ModuleKind, NameBinding, NameBindingKind, ToNameBinding}; +use {Resolver, ResolverArenas}; use Namespace::{self, TypeNS, ValueNS, MacroNS}; use {resolve_error, resolve_struct_error, ResolutionError}; @@ -28,7 +29,7 @@ use rustc::ty; use std::cell::Cell; use std::rc::Rc; -use syntax::ast::Name; +use syntax::ast::{Name, Ident}; use syntax::attr; use syntax::ast::{self, Block, ForeignItem, ForeignItemKind, Item, ItemKind}; @@ -39,30 +40,31 @@ use syntax::ext::base::Determinacy::Undetermined; use syntax::ext::expand::mark_tts; use syntax::ext::hygiene::Mark; use syntax::ext::tt::macro_rules; +use syntax::parse::token; use syntax::symbol::keywords; use syntax::visit::{self, Visitor}; use syntax_pos::{Span, DUMMY_SP}; impl<'a> ToNameBinding<'a> for (Module<'a>, ty::Visibility, Span, Mark) { - fn to_name_binding(self) -> NameBinding<'a> { - NameBinding { + fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> { + arenas.alloc_name_binding(NameBinding { kind: NameBindingKind::Module(self.0), vis: self.1, span: self.2, expansion: self.3, - } + }) } } impl<'a> ToNameBinding<'a> for (Def, ty::Visibility, Span, Mark) { - fn to_name_binding(self) -> NameBinding<'a> { - NameBinding { + fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> { + arenas.alloc_name_binding(NameBinding { kind: NameBindingKind::Def(self.0), vis: self.1, span: self.2, expansion: self.3, - } + }) } } @@ -73,15 +75,15 @@ struct LegacyMacroImports { reexports: Vec<(Name, Span)>, } -impl<'b> Resolver<'b> { +impl<'a> Resolver<'a> { /// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined; /// otherwise, reports an error. - fn define<T>(&mut self, parent: Module<'b>, name: Name, ns: Namespace, def: T) - where T: ToNameBinding<'b>, + fn define<T>(&mut self, parent: Module<'a>, ident: Ident, ns: Namespace, def: T) + where T: ToNameBinding<'a>, { - let binding = def.to_name_binding(); - if let Err(old_binding) = self.try_define(parent, name, ns, binding.clone()) { - self.report_conflict(parent, name, ns, old_binding, &binding); + let binding = def.to_name_binding(self.arenas); + if let Err(old_binding) = self.try_define(parent, ident, ns, binding) { + self.report_conflict(parent, ident, ns, old_binding, &binding); } } @@ -102,7 +104,7 @@ impl<'b> Resolver<'b> { /// Constructs the reduced graph for one item. fn build_reduced_graph_for_item(&mut self, item: &Item, expansion: Mark) { let parent = self.current_module; - let name = item.ident.name; + let ident = item.ident; let sp = item.span; let vis = self.resolve_visibility(&item.vis); @@ -111,7 +113,7 @@ impl<'b> Resolver<'b> { // Extract and intern the module part of the path. For // globs and lists, the path is found directly in the AST; // for simple paths we have to munge the path a little. - let module_path: Vec<_> = match view_path.node { + let mut module_path: Vec<_> = match view_path.node { ViewPathSimple(_, ref full_path) => { full_path.segments .split_last() @@ -131,6 +133,12 @@ impl<'b> Resolver<'b> { } }; + // This can be removed once warning cycle #36888 is complete. + if module_path.len() >= 2 && module_path[0].name == keywords::CrateRoot.name() && + token::Ident(module_path[1]).is_path_segment_keyword() { + module_path.remove(0); + } + // Build up the import directives. let is_prelude = attr::contains_name(&item.attrs, "prelude_import"); @@ -157,8 +165,8 @@ impl<'b> Resolver<'b> { } let subclass = SingleImport { - target: binding.name, - source: source.name, + target: binding, + source: source, result: self.per_ns(|_, _| Cell::new(Err(Undetermined))), }; self.add_import_directive( @@ -187,31 +195,29 @@ impl<'b> Resolver<'b> { for source_item in source_items { let node = source_item.node; - let (module_path, name, rename) = { + let (module_path, ident, rename) = { if node.name.name != keywords::SelfValue.name() { - let rename = node.rename.unwrap_or(node.name).name; - (module_path.clone(), node.name.name, rename) + let rename = node.rename.unwrap_or(node.name); + (module_path.clone(), node.name, rename) } else { - let name = match module_path.last() { - Some(ident) => ident.name, - None => { - resolve_error( - self, - source_item.span, - ResolutionError:: - SelfImportOnlyInImportListWithNonEmptyPrefix - ); - continue; - } - }; + let ident = *module_path.last().unwrap(); + if ident.name == keywords::CrateRoot.name() { + resolve_error( + self, + source_item.span, + ResolutionError:: + SelfImportOnlyInImportListWithNonEmptyPrefix + ); + continue; + } let module_path = module_path.split_last().unwrap().1; - let rename = node.rename.map(|i| i.name).unwrap_or(name); - (module_path.to_vec(), name, rename) + let rename = node.rename.unwrap_or(ident); + (module_path.to_vec(), ident, rename) } }; let subclass = SingleImport { target: rename, - source: name, + source: ident, result: self.per_ns(|_, _| Cell::new(Err(Undetermined))), }; let id = source_item.node.id; @@ -223,7 +229,7 @@ impl<'b> Resolver<'b> { ViewPathGlob(_) => { let subclass = GlobImport { is_prelude: is_prelude, - max_vis: Cell::new(ty::Visibility::PrivateExternal), + max_vis: Cell::new(ty::Visibility::Invisible), }; self.add_import_directive( module_path, subclass, view_path.span, item.id, vis, expansion, @@ -238,8 +244,8 @@ impl<'b> Resolver<'b> { // n.b. we don't need to look at the path option here, because cstore already did let crate_id = self.session.cstore.extern_mod_stmt_cnum(item.id).unwrap(); let module = self.get_extern_crate_root(crate_id); - let binding = (module, ty::Visibility::Public, sp, expansion).to_name_binding(); - let binding = self.arenas.alloc_name_binding(binding); + let binding = + (module, ty::Visibility::Public, sp, expansion).to_name_binding(self.arenas); let directive = self.arenas.alloc_import_directive(ImportDirective { id: item.id, parent: parent, @@ -251,7 +257,7 @@ impl<'b> Resolver<'b> { expansion: expansion, }); let imported_binding = self.import(binding, directive); - self.define(parent, name, TypeNS, imported_binding); + self.define(parent, ident, TypeNS, imported_binding); self.populate_module_if_necessary(module); self.process_legacy_macro_imports(item, module, expansion); } @@ -259,16 +265,16 @@ impl<'b> Resolver<'b> { ItemKind::Mod(..) if item.ident == keywords::Invalid.ident() => {} // Crate root ItemKind::Mod(..) => { - let def = Def::Mod(self.definitions.local_def_id(item.id)); - let module = self.arenas.alloc_module(ModuleS { + let def_id = self.definitions.local_def_id(item.id); + let module_kind = ModuleKind::Def(Def::Mod(def_id), ident.name); + let module = self.arenas.alloc_module(ModuleData { no_implicit_prelude: parent.no_implicit_prelude || { attr::contains_name(&item.attrs, "no_implicit_prelude") }, - normal_ancestor_id: Some(item.id), - ..ModuleS::new(Some(parent), ModuleKind::Def(def, name)) + ..ModuleData::new(Some(parent), module_kind, def_id) }); - self.define(parent, name, TypeNS, (module, vis, sp, expansion)); - self.module_map.insert(item.id, module); + self.define(parent, ident, TypeNS, (module, vis, sp, expansion)); + self.module_map.insert(def_id, module); // Descend into the module. self.current_module = module; @@ -280,27 +286,28 @@ impl<'b> Resolver<'b> { ItemKind::Static(_, m, _) => { let mutbl = m == Mutability::Mutable; let def = Def::Static(self.definitions.local_def_id(item.id), mutbl); - self.define(parent, name, ValueNS, (def, vis, sp, expansion)); + self.define(parent, ident, ValueNS, (def, vis, sp, expansion)); } ItemKind::Const(..) => { let def = Def::Const(self.definitions.local_def_id(item.id)); - self.define(parent, name, ValueNS, (def, vis, sp, expansion)); + self.define(parent, ident, ValueNS, (def, vis, sp, expansion)); } ItemKind::Fn(..) => { let def = Def::Fn(self.definitions.local_def_id(item.id)); - self.define(parent, name, ValueNS, (def, vis, sp, expansion)); + self.define(parent, ident, ValueNS, (def, vis, sp, expansion)); } // These items live in the type namespace. ItemKind::Ty(..) => { let def = Def::TyAlias(self.definitions.local_def_id(item.id)); - self.define(parent, name, TypeNS, (def, vis, sp, expansion)); + self.define(parent, ident, TypeNS, (def, vis, sp, expansion)); } ItemKind::Enum(ref enum_definition, _) => { let def = Def::Enum(self.definitions.local_def_id(item.id)); - let module = self.new_module(parent, ModuleKind::Def(def, name), true); - self.define(parent, name, TypeNS, (module, vis, sp, expansion)); + let module_kind = ModuleKind::Def(def, ident.name); + let module = self.new_module(parent, module_kind, parent.normal_ancestor_id); + self.define(parent, ident, TypeNS, (module, vis, sp, expansion)); for variant in &(*enum_definition).variants { self.build_reduced_graph_for_variant(variant, module, vis, expansion); @@ -311,14 +318,14 @@ impl<'b> Resolver<'b> { ItemKind::Struct(ref struct_def, _) => { // Define a name in the type namespace. let def = Def::Struct(self.definitions.local_def_id(item.id)); - self.define(parent, name, TypeNS, (def, vis, sp, expansion)); + self.define(parent, ident, TypeNS, (def, vis, sp, expansion)); // If this is a tuple or unit struct, define a name // in the value namespace as well. if !struct_def.is_struct() { let ctor_def = Def::StructCtor(self.definitions.local_def_id(struct_def.id()), CtorKind::from_ast(struct_def)); - self.define(parent, name, ValueNS, (ctor_def, vis, sp, expansion)); + self.define(parent, ident, ValueNS, (ctor_def, vis, sp, expansion)); } // Record field names for error reporting. @@ -332,7 +339,7 @@ impl<'b> Resolver<'b> { ItemKind::Union(ref vdata, _) => { let def = Def::Union(self.definitions.local_def_id(item.id)); - self.define(parent, name, TypeNS, (def, vis, sp, expansion)); + self.define(parent, ident, TypeNS, (def, vis, sp, expansion)); // Record field names for error reporting. let field_names = vdata.fields().iter().filter_map(|field| { @@ -349,9 +356,9 @@ impl<'b> Resolver<'b> { let def_id = self.definitions.local_def_id(item.id); // Add all the items within to a new module. - let module = - self.new_module(parent, ModuleKind::Def(Def::Trait(def_id), name), true); - self.define(parent, name, TypeNS, (module, vis, sp, expansion)); + let module_kind = ModuleKind::Def(Def::Trait(def_id), ident.name); + let module = self.new_module(parent, module_kind, parent.normal_ancestor_id); + self.define(parent, ident, TypeNS, (module, vis, sp, expansion)); self.current_module = module; } ItemKind::Mac(_) => panic!("unexpanded macro in resolve!"), @@ -362,29 +369,26 @@ impl<'b> Resolver<'b> { // type and value namespaces. fn build_reduced_graph_for_variant(&mut self, variant: &Variant, - parent: Module<'b>, + parent: Module<'a>, vis: ty::Visibility, expansion: Mark) { - let name = variant.node.name.name; + let ident = variant.node.name; let def_id = self.definitions.local_def_id(variant.node.data.id()); // Define a name in the type namespace. let def = Def::Variant(def_id); - self.define(parent, name, TypeNS, (def, vis, variant.span, expansion)); + self.define(parent, ident, TypeNS, (def, vis, variant.span, expansion)); // Define a constructor name in the value namespace. // Braced variants, unlike structs, generate unusable names in // value namespace, they are reserved for possible future use. let ctor_kind = CtorKind::from_ast(&variant.node.data); let ctor_def = Def::VariantCtor(def_id, ctor_kind); - self.define(parent, name, ValueNS, (ctor_def, vis, variant.span, expansion)); + self.define(parent, ident, ValueNS, (ctor_def, vis, variant.span, expansion)); } /// Constructs the reduced graph for one foreign item. fn build_reduced_graph_for_foreign_item(&mut self, item: &ForeignItem, expansion: Mark) { - let parent = self.current_module; - let name = item.ident.name; - let def = match item.node { ForeignItemKind::Fn(..) => { Def::Fn(self.definitions.local_def_id(item.id)) @@ -393,111 +397,79 @@ impl<'b> Resolver<'b> { Def::Static(self.definitions.local_def_id(item.id), m) } }; + let parent = self.current_module; let vis = self.resolve_visibility(&item.vis); - self.define(parent, name, ValueNS, (def, vis, item.span, expansion)); + self.define(parent, item.ident, ValueNS, (def, vis, item.span, expansion)); } fn build_reduced_graph_for_block(&mut self, block: &Block) { let parent = self.current_module; if self.block_needs_anonymous_module(block) { - let block_id = block.id; - - debug!("(building reduced graph for block) creating a new anonymous module for block \ - {}", - block_id); - - let new_module = self.new_module(parent, ModuleKind::Block(block_id), true); - self.module_map.insert(block_id, new_module); - self.current_module = new_module; // Descend into the block. + let module = + self.new_module(parent, ModuleKind::Block(block.id), parent.normal_ancestor_id); + self.block_map.insert(block.id, module); + self.current_module = module; // Descend into the block. } } /// Builds the reduced graph for a single item in an external crate. - fn build_reduced_graph_for_external_crate_def(&mut self, parent: Module<'b>, child: Export) { - let name = child.name; + fn build_reduced_graph_for_external_crate_def(&mut self, parent: Module<'a>, child: Export) { + let ident = Ident::with_empty_ctxt(child.name); let def = child.def; let def_id = def.def_id(); - let vis = match def { - Def::Macro(..) => ty::Visibility::Public, - _ if parent.is_trait() => ty::Visibility::Public, - _ => self.session.cstore.visibility(def_id), - }; + let vis = self.session.cstore.visibility(def_id); match def { Def::Mod(..) | Def::Enum(..) => { - let module = self.new_module(parent, ModuleKind::Def(def, name), false); - self.define(parent, name, TypeNS, (module, vis, DUMMY_SP, Mark::root())); + let module = self.new_module(parent, ModuleKind::Def(def, ident.name), def_id); + self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, Mark::root())); } - Def::Variant(..) => { - self.define(parent, name, TypeNS, (def, vis, DUMMY_SP, Mark::root())); + Def::Variant(..) | Def::TyAlias(..) => { + self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, Mark::root())); } - Def::VariantCtor(..) => { - self.define(parent, name, ValueNS, (def, vis, DUMMY_SP, Mark::root())); - } - Def::Fn(..) | - Def::Static(..) | - Def::Const(..) | - Def::AssociatedConst(..) | - Def::Method(..) => { - self.define(parent, name, ValueNS, (def, vis, DUMMY_SP, Mark::root())); + Def::Fn(..) | Def::Static(..) | Def::Const(..) | + Def::VariantCtor(..) | Def::StructCtor(..) => { + self.define(parent, ident, ValueNS, (def, vis, DUMMY_SP, Mark::root())); } Def::Trait(..) => { - let module = self.new_module(parent, ModuleKind::Def(def, name), false); - self.define(parent, name, TypeNS, (module, vis, DUMMY_SP, Mark::root())); - - // If this is a trait, add all the trait item names to the trait info. - let trait_item_def_ids = self.session.cstore.associated_item_def_ids(def_id); - for trait_item_def_id in trait_item_def_ids { - let trait_item_name = self.session.cstore.def_key(trait_item_def_id) - .disambiguated_data.data.get_opt_name() - .expect("opt_item_name returned None for trait"); - self.trait_item_map.insert((trait_item_name, def_id), false); + let module_kind = ModuleKind::Def(def, ident.name); + let module = self.new_module(parent, module_kind, parent.normal_ancestor_id); + self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, Mark::root())); + + for child in self.session.cstore.item_children(def_id) { + let ns = if let Def::AssociatedTy(..) = child.def { TypeNS } else { ValueNS }; + let ident = Ident::with_empty_ctxt(child.name); + self.define(module, ident, ns, (child.def, ty::Visibility::Public, + DUMMY_SP, Mark::root())); + + let has_self = self.session.cstore.associated_item(child.def.def_id()) + .map_or(false, |item| item.method_has_self_argument); + self.trait_item_map.insert((def_id, child.name, ns), (child.def, has_self)); } + module.populated.set(true); } - Def::TyAlias(..) | Def::AssociatedTy(..) => { - self.define(parent, name, TypeNS, (def, vis, DUMMY_SP, Mark::root())); - } - Def::Struct(..) => { - self.define(parent, name, TypeNS, (def, vis, DUMMY_SP, Mark::root())); - - // Record field names for error reporting. - let field_names = self.session.cstore.struct_field_names(def_id); - self.insert_field_names(def_id, field_names); - } - Def::StructCtor(..) => { - self.define(parent, name, ValueNS, (def, vis, DUMMY_SP, Mark::root())); - } - Def::Union(..) => { - self.define(parent, name, TypeNS, (def, vis, DUMMY_SP, Mark::root())); + Def::Struct(..) | Def::Union(..) => { + self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, Mark::root())); // Record field names for error reporting. let field_names = self.session.cstore.struct_field_names(def_id); self.insert_field_names(def_id, field_names); } Def::Macro(..) => { - self.define(parent, name, MacroNS, (def, vis, DUMMY_SP, Mark::root())); - } - Def::Local(..) | - Def::PrimTy(..) | - Def::TyParam(..) | - Def::Upvar(..) | - Def::Label(..) | - Def::SelfTy(..) | - Def::Err => { - bug!("unexpected definition: {:?}", def); + self.define(parent, ident, MacroNS, (def, vis, DUMMY_SP, Mark::root())); } + _ => bug!("unexpected definition: {:?}", def) } } - fn get_extern_crate_root(&mut self, cnum: CrateNum) -> Module<'b> { + fn get_extern_crate_root(&mut self, cnum: CrateNum) -> Module<'a> { let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; + let name = self.session.cstore.crate_name(cnum); let macros_only = self.session.cstore.dep_kind(cnum).macros_only(); + let module_kind = ModuleKind::Def(Def::Mod(def_id), name); let arenas = self.arenas; *self.extern_crate_roots.entry((cnum, macros_only)).or_insert_with(|| { - arenas.alloc_module(ModuleS { - populated: Cell::new(false), - ..ModuleS::new(None, ModuleKind::Def(Def::Mod(def_id), keywords::Invalid.name())) - }) + arenas.alloc_module(ModuleData::new(None, module_kind, def_id)) }) } @@ -532,7 +504,7 @@ impl<'b> Resolver<'b> { /// Ensures that the reduced graph rooted at the given external module /// is built, building it if it is not. - pub fn populate_module_if_necessary(&mut self, module: Module<'b>) { + pub fn populate_module_if_necessary(&mut self, module: Module<'a>) { if module.populated.get() { return } for child in self.session.cstore.item_children(module.def_id().unwrap()) { self.build_reduced_graph_for_external_crate_def(module, child); @@ -542,7 +514,7 @@ impl<'b> Resolver<'b> { fn legacy_import_macro(&mut self, name: Name, - binding: &'b NameBinding<'b>, + binding: &'a NameBinding<'a>, span: Span, allow_shadowing: bool) { self.used_crates.insert(binding.def().def_id().krate); @@ -555,7 +527,7 @@ impl<'b> Resolver<'b> { } } - fn process_legacy_macro_imports(&mut self, item: &Item, module: Module<'b>, expansion: Mark) { + fn process_legacy_macro_imports(&mut self, item: &Item, module: Module<'a>, expansion: Mark) { let allow_shadowing = expansion == Mark::root(); let legacy_imports = self.legacy_macro_imports(&item.attrs); let cnum = module.def_id().unwrap().krate; @@ -574,12 +546,13 @@ impl<'b> Resolver<'b> { } if let Some(span) = legacy_imports.import_all { - module.for_each_child(|name, ns, binding| if ns == MacroNS { - self.legacy_import_macro(name, binding, span, allow_shadowing); + module.for_each_child(|ident, ns, binding| if ns == MacroNS { + self.legacy_import_macro(ident.name, binding, span, allow_shadowing); }); } else { for (name, span) in legacy_imports.imports { - let result = self.resolve_name_in_module(module, name, MacroNS, false, None); + let ident = Ident::with_empty_ctxt(name); + let result = self.resolve_ident_in_module(module, ident, MacroNS, false, None); if let Ok(binding) = result { self.legacy_import_macro(name, binding, span, allow_shadowing); } else { @@ -591,7 +564,8 @@ impl<'b> Resolver<'b> { let krate = module.def_id().unwrap().krate; self.used_crates.insert(krate); self.session.cstore.export_macros(krate); - let result = self.resolve_name_in_module(module, name, MacroNS, false, None); + let ident = Ident::with_empty_ctxt(name); + let result = self.resolve_ident_in_module(module, ident, MacroNS, false, None); if let Ok(binding) = result { self.macro_exports.push(Export { name: name, def: binding.def() }); } else { @@ -696,9 +670,13 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> { fn visit_item(&mut self, item: &'a Item) { let macro_use = match item.node { - ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => return, // Scope placeholder - ItemKind::Mac(..) => { - return self.legacy_scope = LegacyScope::Expansion(self.visit_invoc(item.id)); + ItemKind::Mac(ref mac) => { + if mac.node.path.segments.is_empty() { + self.legacy_scope = LegacyScope::Expansion(self.visit_invoc(item.id)); + } else { + self.resolver.define_macro(item, &mut self.legacy_scope); + } + return } ItemKind::Mod(..) => self.resolver.contains_macro_use(&item.attrs), _ => false, @@ -745,21 +723,18 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> { // Add the item to the trait info. let item_def_id = self.resolver.definitions.local_def_id(item.id); - let mut is_static_method = false; - let (def, ns) = match item.node { - TraitItemKind::Const(..) => (Def::AssociatedConst(item_def_id), ValueNS), - TraitItemKind::Method(ref sig, _) => { - is_static_method = !sig.decl.has_self(); - (Def::Method(item_def_id), ValueNS) - } - TraitItemKind::Type(..) => (Def::AssociatedTy(item_def_id), TypeNS), + let (def, ns, has_self) = match item.node { + TraitItemKind::Const(..) => (Def::AssociatedConst(item_def_id), ValueNS, false), + TraitItemKind::Method(ref sig, _) => + (Def::Method(item_def_id), ValueNS, sig.decl.has_self()), + TraitItemKind::Type(..) => (Def::AssociatedTy(item_def_id), TypeNS, false), TraitItemKind::Macro(_) => bug!(), // handled above }; - self.resolver.trait_item_map.insert((item.ident.name, def_id), is_static_method); + self.resolver.trait_item_map.insert((def_id, item.ident.name, ns), (def, has_self)); let vis = ty::Visibility::Public; - self.resolver.define(parent, item.ident.name, ns, (def, vis, item.span, self.expansion)); + self.resolver.define(parent, item.ident, ns, (def, vis, item.span, self.expansion)); self.resolver.current_module = parent.parent.unwrap(); // nearest normal ancestor visit::walk_trait_item(self, item); diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index d54f4e7b20c..2fada8a9ec2 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -860,6 +860,26 @@ match (A, B, C) { ``` "##, +E0422: r##" +You are trying to use an identifier that is either undefined or not a struct. +Erroneous code example: +``` compile_fail,E0422 +fn main () { + let x = Foo { x: 1, y: 2 }; +} +``` +In this case, `Foo` is undefined, so it inherently isn't anything, and +definitely not a struct. +```compile_fail +fn main () { + let foo = 1; + let x = foo { x: 1, y: 2 }; +} +``` +In this case, `foo` is defined, but is not a struct, so Rust can't use it as +one. +"##, + E0423: r##" A `struct` variant name was used like a function name. @@ -1519,7 +1539,12 @@ register_diagnostics! { // E0419, merged into 531 // E0420, merged into 532 // E0421, merged into 531 -// E0422, merged into 531/532 E0531, // unresolved pattern path kind `name` // E0427, merged into 530 + E0573, + E0574, + E0575, + E0576, + E0577, + E0578, } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index f7aaf2475f6..a0af4c45653 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -18,7 +18,6 @@ #![cfg_attr(not(stage0), deny(warnings))] #![feature(associated_consts)] -#![feature(borrow_state)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] #![feature(staged_api)] @@ -34,7 +33,6 @@ extern crate arena; extern crate rustc; use self::Namespace::*; -use self::FallbackSuggestion::*; use self::TypeParameters::*; use self::RibKind::*; @@ -44,14 +42,13 @@ use rustc::middle::cstore::CrateLoader; use rustc::session::Session; use rustc::lint; use rustc::hir::def::*; -use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId}; +use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, LOCAL_CRATE, DefId}; use rustc::ty; use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet}; use syntax::ext::hygiene::{Mark, SyntaxContext}; -use syntax::ast::{self, FloatTy}; -use syntax::ast::{CRATE_NODE_ID, Name, NodeId, Ident, SpannedIdent, IntTy, UintTy}; +use syntax::ast::{self, Name, NodeId, Ident, SpannedIdent, FloatTy, IntTy, UintTy}; use syntax::ext::base::SyntaxExtension; use syntax::ext::base::Determinacy::{Determined, Undetermined}; use syntax::symbol::{Symbol, keywords}; @@ -63,9 +60,9 @@ use syntax::ast::{Arm, BindingMode, Block, Crate, Expr, ExprKind}; use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, Generics}; use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind}; use syntax::ast::{Local, Mutability, Pat, PatKind, Path}; -use syntax::ast::{PathSegment, PathParameters, QSelf, TraitItemKind, TraitRef, Ty, TyKind}; +use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind}; -use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::{Span, DUMMY_SP, MultiSpan}; use errors::DiagnosticBuilder; use std::cell::{Cell, RefCell}; @@ -85,16 +82,16 @@ mod check_unused; mod build_reduced_graph; mod resolve_imports; -enum SuggestionType { - Macro(String), - Function(Symbol), - NotFound, +/// A free importable items suggested in case of resolution failure. +struct ImportSuggestion { + path: Path, } -/// Candidates for a name resolution failure -struct SuggestedCandidates { - name: String, - candidates: Vec<Path>, +/// A field or associated item from self type suggested in case of resolution failure. +enum AssocSuggestion { + Field, + MethodWithSelf, + AssocItem, } enum ResolutionError<'a> { @@ -104,10 +101,6 @@ enum ResolutionError<'a> { OuterTypeParameterContext, /// error E0403: the name is already used for a type parameter in this type parameter list NameAlreadyUsedInTypeParameterList(Name, &'a Span), - /// error E0404: is not a trait - IsNotATrait(&'a str, &'a str), - /// error E0405: use of undeclared trait name - UndeclaredTraitName(&'a str, SuggestedCandidates), /// error E0407: method is not a member of trait MethodNotMemberOfTrait(Name, &'a str), /// error E0437: type is not a member of trait @@ -118,27 +111,10 @@ enum ResolutionError<'a> { VariableNotBoundInPattern(Name, usize, usize), /// error E0409: variable is bound with different mode in pattern #{} than in pattern #1 VariableBoundWithDifferentMode(Name, usize, Span), - /// error E0411: use of `Self` outside of an impl or trait - SelfUsedOutsideImplOrTrait, - /// error E0412: use of undeclared - UseOfUndeclared(&'a str, &'a str, SuggestedCandidates), /// error E0415: identifier is bound more than once in this parameter list IdentifierBoundMoreThanOnceInParameterList(&'a str), /// error E0416: identifier is bound more than once in the same pattern IdentifierBoundMoreThanOnceInSamePattern(&'a str), - /// error E0423: is a struct variant name, but this expression uses it like a function name - StructVariantUsedAsFunction(&'a str), - /// error E0424: `self` is not available in a static method - SelfNotAvailableInStaticMethod, - /// error E0425: unresolved name - UnresolvedName { - path: &'a str, - message: &'a str, - context: UnresolvedNameContext<'a>, - is_static_method: bool, - is_field: bool, - def: Def, - }, /// error E0426: use of undeclared label UndeclaredLabel(&'a str), /// error E0429: `self` imports are only allowed within a { } list @@ -157,37 +133,18 @@ enum ResolutionError<'a> { AttemptToUseNonConstantValueInConstant, /// error E0530: X bindings cannot shadow Ys BindingShadowsSomethingUnacceptable(&'a str, Name, &'a NameBinding<'a>), - /// error E0531: unresolved pattern path kind `name` - PatPathUnresolved(&'a str, &'a Path), - /// error E0532: expected pattern path kind, found another pattern path kind - PatPathUnexpected(&'a str, &'a str, &'a Path), -} - -/// Context of where `ResolutionError::UnresolvedName` arose. -#[derive(Clone, PartialEq, Eq, Debug)] -enum UnresolvedNameContext<'a> { - /// `PathIsMod(parent)` indicates that a given path, used in - /// expression context, actually resolved to a module rather than - /// a value. The optional expression attached to the variant is the - /// the parent of the erroneous path expression. - PathIsMod(Option<&'a Expr>), - - /// `Other` means we have no extra information about the context - /// of the unresolved name error. (Maybe we could eliminate all - /// such cases; but for now, this is an information-free default.) - Other, } -fn resolve_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, - span: syntax_pos::Span, - resolution_error: ResolutionError<'c>) { +fn resolve_error<'sess, 'a>(resolver: &'sess Resolver, + span: Span, + resolution_error: ResolutionError<'a>) { resolve_struct_error(resolver, span, resolution_error).emit(); } -fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, - span: syntax_pos::Span, - resolution_error: ResolutionError<'c>) - -> DiagnosticBuilder<'a> { +fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver, + span: Span, + resolution_error: ResolutionError<'a>) + -> DiagnosticBuilder<'sess> { match resolution_error { ResolutionError::TypeParametersFromOuterFunction => { let mut err = struct_span_err!(resolver.session, @@ -214,26 +171,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, err.span_label(span, &format!("already used")); err.span_label(first_use_span.clone(), &format!("first use of `{}`", name)); err - - } - ResolutionError::IsNotATrait(name, kind_name) => { - let mut err = struct_span_err!(resolver.session, - span, - E0404, - "`{}` is not a trait", - name); - err.span_label(span, &format!("expected trait, found {}", kind_name)); - err - } - ResolutionError::UndeclaredTraitName(name, candidates) => { - let mut err = struct_span_err!(resolver.session, - span, - E0405, - "trait `{}` is not in scope", - name); - show_candidates(&mut err, &candidates); - err.span_label(span, &format!("`{}` is not in scope", name)); - err } ResolutionError::MethodNotMemberOfTrait(method, trait_) => { let mut err = struct_span_err!(resolver.session, @@ -290,25 +227,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, err.span_label(first_binding_span, &format!("first binding")); err } - ResolutionError::SelfUsedOutsideImplOrTrait => { - let mut err = struct_span_err!(resolver.session, - span, - E0411, - "use of `Self` outside of an impl or trait"); - err.span_label(span, &format!("used outside of impl or trait")); - err - } - ResolutionError::UseOfUndeclared(kind, name, candidates) => { - let mut err = struct_span_err!(resolver.session, - span, - E0412, - "{} `{}` is undefined or not in scope", - kind, - name); - show_candidates(&mut err, &candidates); - err.span_label(span, &format!("undefined or not in scope")); - err - } ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { let mut err = struct_span_err!(resolver.session, span, @@ -327,69 +245,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, err.span_label(span, &format!("used in a pattern more than once")); err } - ResolutionError::StructVariantUsedAsFunction(path_name) => { - let mut err = struct_span_err!(resolver.session, - span, - E0423, - "`{}` is the name of a struct or struct variant, but this expression \ - uses it like a function name", - path_name); - err.span_label(span, &format!("struct called like a function")); - err - } - ResolutionError::SelfNotAvailableInStaticMethod => { - let mut err = struct_span_err!(resolver.session, - span, - E0424, - "`self` is not available in a static method"); - err.span_label(span, &format!("not available in static method")); - err.note(&format!("maybe a `self` argument is missing?")); - err - } - ResolutionError::UnresolvedName { path, message: msg, context, is_static_method, - is_field, def } => { - let mut err = struct_span_err!(resolver.session, - span, - E0425, - "unresolved name `{}`", - path); - if msg != "" { - err.span_label(span, &msg); - } else { - err.span_label(span, &format!("unresolved name")); - } - - match context { - UnresolvedNameContext::Other => { - if msg.is_empty() && is_static_method && is_field { - err.help("this is an associated function, you don't have access to \ - this type's fields or methods"); - } - } - UnresolvedNameContext::PathIsMod(parent) => { - err.help(&match parent.map(|parent| &parent.node) { - Some(&ExprKind::Field(_, ident)) => { - format!("to reference an item from the `{module}` module, \ - use `{module}::{ident}`", - module = path, - ident = ident.node) - } - Some(&ExprKind::MethodCall(ident, ..)) => { - format!("to call a function from the `{module}` module, \ - use `{module}::{ident}(..)`", - module = path, - ident = ident.node) - } - _ => { - format!("{def} `{module}` cannot be used as an expression", - def = def.kind_name(), - module = path) - } - }); - } - } - err - } ResolutionError::UndeclaredLabel(name) => { let mut err = struct_span_err!(resolver.session, span, @@ -464,23 +319,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, err.span_label(binding.span, msg); err } - ResolutionError::PatPathUnresolved(expected_what, path) => { - struct_span_err!(resolver.session, - span, - E0531, - "unresolved {} `{}`", - expected_what, - path) - } - ResolutionError::PatPathUnexpected(expected_what, found_what, path) => { - struct_span_err!(resolver.session, - span, - E0532, - "expected {}, found {} `{}`", - expected_what, - found_what, - path) - } } } @@ -522,6 +360,163 @@ impl PatternSource { } } +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum PathSource<'a> { + // Type paths `Path`. + Type, + // Trait paths in bounds or impls. + Trait, + // Expression paths `path`, with optional parent context. + Expr(Option<&'a ExprKind>), + // Paths in path patterns `Path`. + Pat, + // Paths in struct expressions and patterns `Path { .. }`. + Struct, + // Paths in tuple struct patterns `Path(..)`. + TupleStruct, + // `m::A::B` in `<T as m::A>::B::C`. + TraitItem(Namespace), + // Path in `pub(path)` + Visibility, + // Path in `use a::b::{...};` + ImportPrefix, +} + +impl<'a> PathSource<'a> { + fn namespace(self) -> Namespace { + match self { + PathSource::Type | PathSource::Trait | PathSource::Struct | + PathSource::Visibility | PathSource::ImportPrefix => TypeNS, + PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct => ValueNS, + PathSource::TraitItem(ns) => ns, + } + } + + fn global_by_default(self) -> bool { + match self { + PathSource::Visibility | PathSource::ImportPrefix => true, + PathSource::Type | PathSource::Expr(..) | PathSource::Pat | + PathSource::Struct | PathSource::TupleStruct | + PathSource::Trait | PathSource::TraitItem(..) => false, + } + } + + fn defer_to_typeck(self) -> bool { + match self { + PathSource::Type | PathSource::Expr(..) | PathSource::Pat | + PathSource::Struct | PathSource::TupleStruct => true, + PathSource::Trait | PathSource::TraitItem(..) | + PathSource::Visibility | PathSource::ImportPrefix => false, + } + } + + fn descr_expected(self) -> &'static str { + match self { + PathSource::Type => "type", + PathSource::Trait => "trait", + PathSource::Pat => "unit struct/variant or constant", + PathSource::Struct => "struct, variant or union type", + PathSource::TupleStruct => "tuple struct/variant", + PathSource::Visibility => "module", + PathSource::ImportPrefix => "module or enum", + PathSource::TraitItem(ns) => match ns { + TypeNS => "associated type", + ValueNS => "method or associated constant", + MacroNS => bug!("associated macro"), + }, + PathSource::Expr(parent) => match parent { + // "function" here means "anything callable" rather than `Def::Fn`, + // this is not precise but usually more helpful than just "value". + Some(&ExprKind::Call(..)) => "function", + _ => "value", + }, + } + } + + fn is_expected(self, def: Def) -> bool { + match self { + PathSource::Type => match def { + Def::Struct(..) | Def::Union(..) | Def::Enum(..) | + Def::Trait(..) | Def::TyAlias(..) | Def::AssociatedTy(..) | + Def::PrimTy(..) | Def::TyParam(..) | Def::SelfTy(..) => true, + _ => false, + }, + PathSource::Trait => match def { + Def::Trait(..) => true, + _ => false, + }, + PathSource::Expr(..) => match def { + Def::StructCtor(_, CtorKind::Const) | Def::StructCtor(_, CtorKind::Fn) | + Def::VariantCtor(_, CtorKind::Const) | Def::VariantCtor(_, CtorKind::Fn) | + Def::Const(..) | Def::Static(..) | Def::Local(..) | Def::Upvar(..) | + Def::Fn(..) | Def::Method(..) | Def::AssociatedConst(..) => true, + _ => false, + }, + PathSource::Pat => match def { + Def::StructCtor(_, CtorKind::Const) | + Def::VariantCtor(_, CtorKind::Const) | + Def::Const(..) | Def::AssociatedConst(..) => true, + _ => false, + }, + PathSource::TupleStruct => match def { + Def::StructCtor(_, CtorKind::Fn) | Def::VariantCtor(_, CtorKind::Fn) => true, + _ => false, + }, + PathSource::Struct => match def { + Def::Struct(..) | Def::Union(..) | Def::Variant(..) | + Def::TyAlias(..) | Def::AssociatedTy(..) | Def::SelfTy(..) => true, + _ => false, + }, + PathSource::TraitItem(ns) => match def { + Def::AssociatedConst(..) | Def::Method(..) if ns == ValueNS => true, + Def::AssociatedTy(..) if ns == TypeNS => true, + _ => false, + }, + PathSource::ImportPrefix => match def { + Def::Mod(..) | Def::Enum(..) => true, + _ => false, + }, + PathSource::Visibility => match def { + Def::Mod(..) => true, + _ => false, + }, + } + } + + fn error_code(self, has_unexpected_resolution: bool) -> &'static str { + __diagnostic_used!(E0404); + __diagnostic_used!(E0405); + __diagnostic_used!(E0412); + __diagnostic_used!(E0422); + __diagnostic_used!(E0423); + __diagnostic_used!(E0425); + __diagnostic_used!(E0531); + __diagnostic_used!(E0532); + __diagnostic_used!(E0573); + __diagnostic_used!(E0574); + __diagnostic_used!(E0575); + __diagnostic_used!(E0576); + __diagnostic_used!(E0577); + __diagnostic_used!(E0578); + match (self, has_unexpected_resolution) { + (PathSource::Trait, true) => "E0404", + (PathSource::Trait, false) => "E0405", + (PathSource::Type, true) => "E0573", + (PathSource::Type, false) => "E0412", + (PathSource::Struct, true) => "E0574", + (PathSource::Struct, false) => "E0422", + (PathSource::Expr(..), true) => "E0423", + (PathSource::Expr(..), false) => "E0425", + (PathSource::Pat, true) | (PathSource::TupleStruct, true) => "E0532", + (PathSource::Pat, false) | (PathSource::TupleStruct, false) => "E0531", + (PathSource::TraitItem(..), true) => "E0575", + (PathSource::TraitItem(..), false) => "E0576", + (PathSource::Visibility, true) | (PathSource::ImportPrefix, true) => "E0577", + (PathSource::Visibility, false) | (PathSource::ImportPrefix, false) => "E0578", + } + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum Namespace { TypeNS, @@ -574,15 +569,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { self.resolve_local(local); } fn visit_ty(&mut self, ty: &'tcx Ty) { - self.resolve_type(ty); + if let TyKind::Path(ref qself, ref path) = ty.node { + self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type); + } + visit::walk_ty(self, ty); } fn visit_poly_trait_ref(&mut self, tref: &'tcx ast::PolyTraitRef, m: &'tcx ast::TraitBoundModifier) { - let ast::Path { ref segments, span, global } = tref.trait_ref.path; - let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect(); - let def = self.resolve_trait_reference(&path, global, None, span); - self.record_def(tref.trait_ref.ref_id, def); + self.smart_resolve_path(tref.trait_ref.ref_id, None, + &tref.trait_ref.path, PathSource::Trait); visit::walk_poly_trait_ref(self, tref, m); } fn visit_variant(&mut self, @@ -668,13 +664,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> { pub type ErrorMessage = Option<(Span, String)>; -enum FallbackSuggestion { - NoSuggestion, - Field, - TraitItem, - TraitMethod(String), -} - #[derive(Copy, Clone)] enum TypeParameters<'a, 'b> { NoTypeParameters, @@ -734,7 +723,7 @@ impl<'a> Rib<'a> { } /// A definition along with the index of the rib it was found on -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] struct LocalDef { ribs: Option<(Namespace, usize)>, def: Def, @@ -754,13 +743,6 @@ impl<'a> LexicalScopeBinding<'a> { } } -#[derive(Copy, Clone)] -enum PathScope { - Global, - Lexical, - Import, -} - #[derive(Clone)] enum PathResult<'a> { Module(Module<'a>), @@ -775,16 +757,16 @@ enum ModuleKind { } /// One node in the tree of modules. -pub struct ModuleS<'a> { +pub struct ModuleData<'a> { parent: Option<Module<'a>>, kind: ModuleKind, - // The node id of the closest normal module (`mod`) ancestor (including this module). - normal_ancestor_id: Option<NodeId>, + // The def id of the closest normal module (`mod`) ancestor (including this module). + normal_ancestor_id: DefId, - resolutions: RefCell<FxHashMap<(Name, Namespace), &'a RefCell<NameResolution<'a>>>>, - legacy_macro_resolutions: RefCell<Vec<(Mark, Name, Span)>>, - macro_resolutions: RefCell<Vec<(Box<[Ident]>, PathScope, Span)>>, + resolutions: RefCell<FxHashMap<(Ident, Namespace), &'a RefCell<NameResolution<'a>>>>, + legacy_macro_resolutions: RefCell<Vec<(Mark, Ident, Span)>>, + macro_resolutions: RefCell<Vec<(Box<[Ident]>, Span)>>, // Macro invocations that can expand into items in this module. unresolved_invocations: RefCell<FxHashSet<Mark>>, @@ -795,7 +777,7 @@ pub struct ModuleS<'a> { globs: RefCell<Vec<&'a ImportDirective<'a>>>, // Used to memoize the traits in this module for faster searches through all traits in scope. - traits: RefCell<Option<Box<[(Name, &'a NameBinding<'a>)]>>>, + traits: RefCell<Option<Box<[(Ident, &'a NameBinding<'a>)]>>>, // Whether this module is populated. If not populated, any attempt to // access the children must be preceded with a @@ -803,14 +785,14 @@ pub struct ModuleS<'a> { populated: Cell<bool>, } -pub type Module<'a> = &'a ModuleS<'a>; +pub type Module<'a> = &'a ModuleData<'a>; -impl<'a> ModuleS<'a> { - fn new(parent: Option<Module<'a>>, kind: ModuleKind) -> Self { - ModuleS { +impl<'a> ModuleData<'a> { + fn new(parent: Option<Module<'a>>, kind: ModuleKind, normal_ancestor_id: DefId) -> Self { + ModuleData { parent: parent, kind: kind, - normal_ancestor_id: None, + normal_ancestor_id: normal_ancestor_id, resolutions: RefCell::new(FxHashMap()), legacy_macro_resolutions: RefCell::new(Vec::new()), macro_resolutions: RefCell::new(Vec::new()), @@ -819,13 +801,13 @@ impl<'a> ModuleS<'a> { glob_importers: RefCell::new(Vec::new()), globs: RefCell::new((Vec::new())), traits: RefCell::new(None), - populated: Cell::new(true), + populated: Cell::new(normal_ancestor_id.is_local()), } } - fn for_each_child<F: FnMut(Name, Namespace, &'a NameBinding<'a>)>(&self, mut f: F) { - for (&(name, ns), name_resolution) in self.resolutions.borrow().iter() { - name_resolution.borrow().binding.map(|binding| f(name, ns, binding)); + fn for_each_child<F: FnMut(Ident, Namespace, &'a NameBinding<'a>)>(&self, mut f: F) { + for (&(ident, ns), name_resolution) in self.resolutions.borrow().iter() { + name_resolution.borrow().binding.map(|binding| f(ident, ns, binding)); } } @@ -856,11 +838,11 @@ impl<'a> ModuleS<'a> { } fn is_local(&self) -> bool { - self.normal_ancestor_id.is_some() + self.normal_ancestor_id.is_local() } } -impl<'a> fmt::Debug for ModuleS<'a> { +impl<'a> fmt::Debug for ModuleData<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.def()) } @@ -876,11 +858,11 @@ pub struct NameBinding<'a> { } pub trait ToNameBinding<'a> { - fn to_name_binding(self) -> NameBinding<'a>; + fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a>; } -impl<'a> ToNameBinding<'a> for NameBinding<'a> { - fn to_name_binding(self) -> NameBinding<'a> { +impl<'a> ToNameBinding<'a> for &'a NameBinding<'a> { + fn to_name_binding(self, _: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> { self } } @@ -897,6 +879,7 @@ enum NameBindingKind<'a> { Ambiguity { b1: &'a NameBinding<'a>, b2: &'a NameBinding<'a>, + legacy: bool, } } @@ -908,6 +891,7 @@ struct AmbiguityError<'a> { lexical: bool, b1: &'a NameBinding<'a>, b2: &'a NameBinding<'a>, + legacy: bool, } impl<'a> NameBinding<'a> { @@ -915,6 +899,7 @@ impl<'a> NameBinding<'a> { match self.kind { NameBindingKind::Module(module) => Some(module), NameBindingKind::Import { binding, .. } => binding.module(), + NameBindingKind::Ambiguity { legacy: true, b1, .. } => b1.module(), _ => None, } } @@ -924,6 +909,7 @@ impl<'a> NameBinding<'a> { NameBindingKind::Def(def) => def, NameBindingKind::Module(module) => module.def().unwrap(), NameBindingKind::Import { binding, .. } => binding.def(), + NameBindingKind::Ambiguity { legacy: true, b1, .. } => b1.def(), NameBindingKind::Ambiguity { .. } => Def::Err, } } @@ -1030,7 +1016,7 @@ pub struct Resolver<'a> { prelude: Option<Module<'a>>, - trait_item_map: FxHashMap<(Name, DefId), bool /* is static method? */>, + trait_item_map: FxHashMap<(DefId, Name, Namespace), (Def, bool /* has self */)>, // Names of fields of an item `DefId` accessible with dot syntax. // Used for hints during error reporting. @@ -1067,7 +1053,7 @@ pub struct Resolver<'a> { pub export_map: ExportMap, pub trait_map: TraitMap, - // A map from nodes to modules, both normal (`mod`) modules and anonymous modules. + // A map from nodes to anonymous modules. // Anonymous modules are pseudo-modules that are implicitly created around items // contained within blocks. // @@ -1081,7 +1067,8 @@ pub struct Resolver<'a> { // // There will be an anonymous module created around `g` with the ID of the // entry block for `f`. - module_map: NodeMap<Module<'a>>, + block_map: NodeMap<Module<'a>>, + module_map: FxHashMap<DefId, Module<'a>>, extern_crate_roots: FxHashMap<(CrateNum, bool /* MacrosOnly? */), Module<'a>>, pub make_glob_map: bool, @@ -1108,6 +1095,7 @@ pub struct Resolver<'a> { lexical_macro_resolutions: Vec<(Name, &'a Cell<LegacyScope<'a>>)>, macro_map: FxHashMap<DefId, Rc<SyntaxExtension>>, macro_exports: Vec<Export>, + pub whitelisted_legacy_custom_derives: Vec<Name>, // Maps the `Mark` of an expansion to its containing module or block. invocations: FxHashMap<Mark, &'a InvocationData<'a>>, @@ -1117,7 +1105,7 @@ pub struct Resolver<'a> { } pub struct ResolverArenas<'a> { - modules: arena::TypedArena<ModuleS<'a>>, + modules: arena::TypedArena<ModuleData<'a>>, local_modules: RefCell<Vec<Module<'a>>>, name_bindings: arena::TypedArena<NameBinding<'a>>, import_directives: arena::TypedArena<ImportDirective<'a>>, @@ -1127,7 +1115,7 @@ pub struct ResolverArenas<'a> { } impl<'a> ResolverArenas<'a> { - fn alloc_module(&'a self, module: ModuleS<'a>) -> Module<'a> { + fn alloc_module(&'a self, module: ModuleData<'a>) -> Module<'a> { let module = self.modules.alloc(module); if module.def_id().map(|def_id| def_id.is_local()).unwrap_or(true) { self.local_modules.borrow_mut().push(module); @@ -1156,28 +1144,24 @@ impl<'a> ResolverArenas<'a> { } } -impl<'a> ty::NodeIdTree for Resolver<'a> { - fn is_descendant_of(&self, mut node: NodeId, ancestor: NodeId) -> bool { - while node != ancestor { - node = match self.module_map[&node].parent { - Some(parent) => parent.normal_ancestor_id.unwrap(), - None => return false, - } - } - true +impl<'a, 'b: 'a> ty::DefIdTree for &'a Resolver<'b> { + fn parent(self, id: DefId) -> Option<DefId> { + match id.krate { + LOCAL_CRATE => self.definitions.def_key(id.index).parent, + _ => self.session.cstore.def_key(id).parent, + }.map(|index| DefId { index: index, ..id }) } } impl<'a> hir::lowering::Resolver for Resolver<'a> { fn resolve_hir_path(&mut self, path: &mut hir::Path, is_value: bool) { let namespace = if is_value { ValueNS } else { TypeNS }; - let hir::Path { ref segments, span, global, ref mut def } = *path; + let hir::Path { ref segments, span, ref mut def } = *path; let path: Vec<_> = segments.iter().map(|seg| Ident::with_empty_ctxt(seg.name)).collect(); - let scope = if global { PathScope::Global } else { PathScope::Lexical }; - match self.resolve_path(&path, scope, Some(namespace), Some(span)) { + match self.resolve_path(&path, Some(namespace), Some(span)) { PathResult::Module(module) => *def = module.def().unwrap(), PathResult::NonModule(path_res) if path_res.depth == 0 => *def = path_res.base_def, - PathResult::NonModule(..) => match self.resolve_path(&path, scope, None, Some(span)) { + PathResult::NonModule(..) => match self.resolve_path(&path, None, Some(span)) { PathResult::Failed(msg, _) => { resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); } @@ -1206,14 +1190,14 @@ impl<'a> Resolver<'a> { crate_loader: &'a mut CrateLoader, arenas: &'a ResolverArenas<'a>) -> Resolver<'a> { - let root_def = Def::Mod(DefId::local(CRATE_DEF_INDEX)); - let graph_root = arenas.alloc_module(ModuleS { - normal_ancestor_id: Some(CRATE_NODE_ID), + let root_def_id = DefId::local(CRATE_DEF_INDEX); + let root_module_kind = ModuleKind::Def(Def::Mod(root_def_id), keywords::Invalid.name()); + let graph_root = arenas.alloc_module(ModuleData { no_implicit_prelude: attr::contains_name(&krate.attrs, "no_implicit_prelude"), - ..ModuleS::new(None, ModuleKind::Def(root_def, keywords::Invalid.name())) + ..ModuleData::new(None, root_module_kind, root_def_id) }); - let mut module_map = NodeMap(); - module_map.insert(CRATE_NODE_ID, graph_root); + let mut module_map = FxHashMap(); + module_map.insert(DefId::local(CRATE_DEF_INDEX), graph_root); let mut definitions = Definitions::new(); DefCollector::new(&mut definitions).collect_root(); @@ -1258,6 +1242,7 @@ impl<'a> Resolver<'a> { export_map: NodeMap(), trait_map: NodeMap(), module_map: module_map, + block_map: NodeMap(), extern_crate_roots: FxHashMap(), make_glob_map: make_glob_map == MakeGlobMap::Yes, @@ -1289,6 +1274,7 @@ impl<'a> Resolver<'a> { macro_exports: Vec::new(), invocations: invocations, name_already_seen: FxHashMap(), + whitelisted_legacy_custom_derives: Vec::new(), } } @@ -1327,18 +1313,15 @@ impl<'a> Resolver<'a> { self.crate_loader.postprocess(krate); } - fn new_module(&self, parent: Module<'a>, kind: ModuleKind, local: bool) -> Module<'a> { - self.arenas.alloc_module(ModuleS { - normal_ancestor_id: if local { self.current_module.normal_ancestor_id } else { None }, - populated: Cell::new(local), - ..ModuleS::new(Some(parent), kind) - }) + fn new_module(&self, parent: Module<'a>, kind: ModuleKind, normal_ancestor_id: DefId) + -> Module<'a> { + self.arenas.alloc_module(ModuleData::new(Some(parent), kind, normal_ancestor_id)) } - fn record_use(&mut self, name: Name, ns: Namespace, binding: &'a NameBinding<'a>, span: Span) + fn record_use(&mut self, ident: Ident, ns: Namespace, binding: &'a NameBinding<'a>, span: Span) -> bool /* true if an error was reported */ { // track extern crates for unused_extern_crate lint - if let Some(DefId { krate, .. }) = binding.module().and_then(ModuleS::def_id) { + if let Some(DefId { krate, .. }) = binding.module().and_then(ModuleData::def_id) { self.used_crates.insert(krate); } @@ -1346,23 +1329,26 @@ impl<'a> Resolver<'a> { NameBindingKind::Import { directive, binding, ref used } if !used.get() => { used.set(true); self.used_imports.insert((directive.id, ns)); - self.add_to_glob_map(directive.id, name); - self.record_use(name, ns, binding, span) + self.add_to_glob_map(directive.id, ident); + self.record_use(ident, ns, binding, span) } NameBindingKind::Import { .. } => false, - NameBindingKind::Ambiguity { b1, b2 } => { + NameBindingKind::Ambiguity { b1, b2, legacy } => { self.ambiguity_errors.push(AmbiguityError { - span: span, name: name, lexical: false, b1: b1, b2: b2, + span: span, name: ident.name, lexical: false, b1: b1, b2: b2, legacy: legacy, }); - true + if legacy { + self.record_use(ident, ns, b1, span); + } + !legacy } _ => false } } - fn add_to_glob_map(&mut self, id: NodeId, name: Name) { + fn add_to_glob_map(&mut self, id: NodeId, ident: Ident) { if self.make_glob_map { - self.glob_map.entry(id).or_insert_with(FxHashSet).insert(name); + self.glob_map.entry(id).or_insert_with(FxHashSet).insert(ident.name); } } @@ -1389,23 +1375,20 @@ impl<'a> Resolver<'a> { record_used: Option<Span>) -> Option<LexicalScopeBinding<'a>> { if ns == TypeNS { - ident = Ident::with_empty_ctxt(ident.name); + ident = ident.unhygienize(); } // Walk backwards up the ribs in scope. for i in (0 .. self.ribs[ns].len()).rev() { if let Some(def) = self.ribs[ns][i].bindings.get(&ident).cloned() { // The ident resolves to a type parameter or local variable. - return Some(LexicalScopeBinding::Def(if let Some(span) = record_used { - self.adjust_local_def(LocalDef { ribs: Some((ns, i)), def: def }, span) - } else { - def - })); + return Some(LexicalScopeBinding::Def( + self.adjust_local_def(LocalDef { ribs: Some((ns, i)), def: def }, record_used) + )); } if let ModuleRibKind(module) = self.ribs[ns][i].kind { - let name = ident.name; - let item = self.resolve_name_in_module(module, name, ns, false, record_used); + let item = self.resolve_ident_in_module(module, ident, ns, false, record_used); if let Ok(binding) = item { // The ident resolves to an item. return Some(LexicalScopeBinding::Item(binding)); @@ -1414,7 +1397,7 @@ impl<'a> Resolver<'a> { if let ModuleKind::Block(..) = module.kind { // We can see through blocks } else if !module.no_implicit_prelude { return self.prelude.and_then(|prelude| { - self.resolve_name_in_module(prelude, name, ns, false, None).ok() + self.resolve_ident_in_module(prelude, ident, ns, false, None).ok() }).map(LexicalScopeBinding::Item) } else { return None; @@ -1463,6 +1446,7 @@ impl<'a> Resolver<'a> { fn with_scope<F>(&mut self, id: NodeId, f: F) where F: FnOnce(&mut Resolver) { + let id = self.definitions.local_def_id(id); let module = self.module_map.get(&id).cloned(); // clones a reference if let Some(module) = module { // Move down in the graph. @@ -1526,7 +1510,7 @@ impl<'a> Resolver<'a> { } ItemKind::DefaultImpl(_, ref trait_ref) => { - self.with_optional_trait_ref(Some(trait_ref), |_, _| {}, None); + self.with_optional_trait_ref(Some(trait_ref), |_, _| {}); } ItemKind::Impl(.., ref generics, ref opt_trait_ref, ref self_type, ref impl_items) => self.resolve_implementation(generics, @@ -1591,34 +1575,9 @@ impl<'a> Resolver<'a> { ItemKind::Use(ref view_path) => { match view_path.node { - ast::ViewPathList(ref prefix, ref items) => { - let path: Vec<_> = - prefix.segments.iter().map(|seg| seg.identifier).collect(); - // Resolve prefix of an import with empty braces (issue #28388) - if items.is_empty() && !prefix.segments.is_empty() { - let (scope, span) = (PathScope::Import, prefix.span); - // FIXME(#38012) This should be a module path, not anything in TypeNS. - let result = - self.resolve_path(&path, scope, Some(TypeNS), Some(span)); - let (def, msg) = match result { - PathResult::Module(module) => (module.def().unwrap(), None), - PathResult::NonModule(res) if res.depth == 0 => - (res.base_def, None), - PathResult::NonModule(_) => { - // Resolve a module path for better errors - match self.resolve_path(&path, scope, None, Some(span)) { - PathResult::Failed(msg, _) => (Def::Err, Some(msg)), - _ => unreachable!(), - } - } - PathResult::Indeterminate => unreachable!(), - PathResult::Failed(msg, _) => (Def::Err, Some(msg)), - }; - if let Some(msg) = msg { - resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); - } - self.record_def(item.id, PathResolution::new(def)); - } + ast::ViewPathList(ref prefix, ref items) if items.is_empty() => { + // Resolve prefix of an import with empty braces (issue #28388). + self.smart_resolve_path(item.id, None, prefix, PathSource::ImportPrefix); } _ => {} } @@ -1691,56 +1650,6 @@ impl<'a> Resolver<'a> { self.ribs[ValueNS].pop(); } - fn resolve_trait_reference(&mut self, - path: &[Ident], - global: bool, - generics: Option<&Generics>, - span: Span) - -> PathResolution { - let scope = if global { PathScope::Global } else { PathScope::Lexical }; - let def = match self.resolve_path(path, scope, None, Some(span)) { - PathResult::Module(module) => Some(module.def().unwrap()), - PathResult::NonModule(..) => return err_path_resolution(), - PathResult::Failed(msg, false) => { - resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); - return err_path_resolution(); - } - _ => match self.resolve_path(path, scope, Some(TypeNS), None) { - PathResult::NonModule(path_resolution) => Some(path_resolution.base_def), - _ => None, - }, - }; - - if let Some(def) = def { - if let Def::Trait(_) = def { - return PathResolution::new(def); - } - - let mut err = resolve_struct_error(self, span, { - ResolutionError::IsNotATrait(&names_to_string(path), def.kind_name()) - }); - if let Some(generics) = generics { - if let Some(span) = generics.span_for_name(&names_to_string(path)) { - err.span_label(span, &"type parameter defined here"); - } - } - - // If it's a typedef, give a note - if let Def::TyAlias(..) = def { - err.note(&format!("type aliases cannot be used for traits")); - } - err.emit(); - } else { - // find possible candidates - let is_trait = |def| match def { Def::Trait(_) => true, _ => false }; - let candidates = self.lookup_candidates(path.last().unwrap().name, TypeNS, is_trait); - - let path = names_to_string(path); - resolve_error(self, span, ResolutionError::UndeclaredTraitName(&path, candidates)); - } - err_path_resolution() - } - fn with_current_self_type<T, F>(&mut self, self_type: &Ty, f: F) -> T where F: FnOnce(&mut Resolver) -> T { @@ -1751,24 +1660,17 @@ impl<'a> Resolver<'a> { result } - fn with_optional_trait_ref<T, F>(&mut self, - opt_trait_ref: Option<&TraitRef>, - f: F, - generics: Option<&Generics>) - -> T + fn with_optional_trait_ref<T, F>(&mut self, opt_trait_ref: Option<&TraitRef>, f: F) -> T where F: FnOnce(&mut Resolver, Option<DefId>) -> T { let mut new_val = None; let mut new_id = None; if let Some(trait_ref) = opt_trait_ref { - let ast::Path { ref segments, span, global } = trait_ref.path; - let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect(); - let path_res = self.resolve_trait_reference(&path, global, generics, span); - assert!(path_res.depth == 0); - self.record_def(trait_ref.ref_id, path_res); - if path_res.base_def != Def::Err { - new_val = Some((path_res.base_def.def_id(), trait_ref.clone())); - new_id = Some(path_res.base_def.def_id()); + let def = self.smart_resolve_path(trait_ref.ref_id, None, + &trait_ref.path, PathSource::Trait).base_def; + if def != Def::Err { + new_val = Some((def.def_id(), trait_ref.clone())); + new_id = Some(def.def_id()); } visit::walk_trait_ref(self, trait_ref); } @@ -1816,6 +1718,7 @@ impl<'a> Resolver<'a> { // If this is a trait impl, ensure the const // exists in trait this.check_trait_item(impl_item.ident.name, + ValueNS, impl_item.span, |n, s| ResolutionError::ConstNotMemberOfTrait(n, s)); visit::walk_impl_item(this, impl_item); @@ -1824,6 +1727,7 @@ impl<'a> Resolver<'a> { // If this is a trait impl, ensure the method // exists in trait this.check_trait_item(impl_item.ident.name, + ValueNS, impl_item.span, |n, s| ResolutionError::MethodNotMemberOfTrait(n, s)); @@ -1840,6 +1744,7 @@ impl<'a> Resolver<'a> { // If this is a trait impl, ensure the type // exists in trait this.check_trait_item(impl_item.ident.name, + TypeNS, impl_item.span, |n, s| ResolutionError::TypeNotMemberOfTrait(n, s)); @@ -1850,18 +1755,18 @@ impl<'a> Resolver<'a> { } }); }); - }, Some(&generics)); + }); }); } - fn check_trait_item<F>(&self, name: Name, span: Span, err: F) + fn check_trait_item<F>(&self, name: Name, ns: Namespace, span: Span, err: F) where F: FnOnce(Name, &str) -> ResolutionError { // If there is a TraitRef in scope for an impl, then the method must be in the // trait. if let Some((did, ref trait_ref)) = self.current_trait_ref { - if !self.trait_item_map.contains_key(&(name, did)) { - let path_str = path_names_to_string(&trait_ref.path, 0); + if !self.trait_item_map.contains_key(&(did, name, ns)) { + let path_str = path_names_to_string(&trait_ref.path); resolve_error(self, span, err(name, &path_str)); } } @@ -1962,7 +1867,7 @@ impl<'a> Resolver<'a> { debug!("(resolving block) entering block"); // Move down in the graph, if there's an anonymous module rooted here. let orig_module = self.current_module; - let anonymous_module = self.module_map.get(&block.id).cloned(); // clones a reference + let anonymous_module = self.block_map.get(&block.id).cloned(); // clones a reference let mut num_macro_definition_ribs = 0; if let Some(anonymous_module) = anonymous_module { @@ -2001,58 +1906,6 @@ impl<'a> Resolver<'a> { debug!("(resolving block) leaving block"); } - fn resolve_type(&mut self, ty: &Ty) { - if let TyKind::Path(ref maybe_qself, ref path) = ty.node { - // This is a path in the type namespace. Walk through scopes looking for it. - if let Some(def) = - self.resolve_possibly_assoc_item(ty.id, maybe_qself.as_ref(), path, TypeNS) { - match def.base_def { - Def::Mod(..) if def.depth == 0 => { - self.session.span_err(path.span, "expected type, found module"); - self.record_def(ty.id, err_path_resolution()); - } - _ => { - // Write the result into the def map. - debug!("(resolving type) writing resolution for `{}` (id {}) = {:?}", - path_names_to_string(path, 0), ty.id, def); - self.record_def(ty.id, def); - } - } - } else { - self.record_def(ty.id, err_path_resolution()); - // Keep reporting some errors even if they're ignored above. - let kind = if maybe_qself.is_some() { "associated type" } else { "type name" }; - let is_invalid_self_type_name = { - path.segments.len() > 0 && - maybe_qself.is_none() && - path.segments[0].identifier.name == keywords::SelfType.name() - }; - - if is_invalid_self_type_name { - resolve_error(self, ty.span, ResolutionError::SelfUsedOutsideImplOrTrait); - } else { - let type_name = path.segments.last().unwrap().identifier.name; - let candidates = self.lookup_candidates(type_name, TypeNS, |def| { - match def { - Def::Trait(_) | - Def::Enum(_) | - Def::Struct(_) | - Def::Union(_) | - Def::TyAlias(_) => true, - _ => false, - } - }); - - let name = &path_names_to_string(path, 0); - let error = ResolutionError::UseOfUndeclared(kind, name, candidates); - resolve_error(self, ty.span, error); - } - } - } - // Resolve embedded types. - visit::walk_ty(self, ty); - } - fn fresh_binding(&mut self, ident: &SpannedIdent, pat_id: NodeId, @@ -2106,61 +1959,6 @@ impl<'a> Resolver<'a> { PathResolution::new(def) } - fn resolve_pattern_path<ExpectedFn>(&mut self, - pat_id: NodeId, - qself: Option<&QSelf>, - path: &Path, - namespace: Namespace, - expected_fn: ExpectedFn, - expected_what: &str) - where ExpectedFn: FnOnce(Def) -> bool - { - let resolution = if let Some(resolution) = self.resolve_possibly_assoc_item(pat_id, - qself, path, namespace) { - if resolution.depth == 0 { - if expected_fn(resolution.base_def) || resolution.base_def == Def::Err { - resolution - } else { - resolve_error( - self, - path.span, - ResolutionError::PatPathUnexpected(expected_what, - resolution.kind_name(), path) - ); - err_path_resolution() - } - } else { - // Not fully resolved associated item `T::A::B` or `<T as Tr>::A::B` - // or `<T>::A::B`. If `B` should be resolved in value namespace then - // it needs to be added to the trait map. - if namespace == ValueNS { - let item_name = path.segments.last().unwrap().identifier.name; - let traits = self.get_traits_containing_item(item_name); - self.trait_map.insert(pat_id, traits); - } - resolution - } - } else { - let error = ResolutionError::PatPathUnresolved(expected_what, path); - resolve_error(self, path.span, error); - err_path_resolution() - }; - - self.record_def(pat_id, resolution); - } - - fn resolve_struct_path(&mut self, node_id: NodeId, path: &Path) { - // Resolution logic is equivalent for expressions and patterns, - // reuse `resolve_pattern_path` for both. - self.resolve_pattern_path(node_id, None, path, TypeNS, |def| { - match def { - Def::Struct(..) | Def::Union(..) | Def::Variant(..) | - Def::TyAlias(..) | Def::AssociatedTy(..) | Def::SelfTy(..) => true, - _ => false, - } - }, "struct, variant or union type"); - } - fn resolve_pattern(&mut self, pat: &Pat, pat_src: PatternSource, @@ -2184,8 +1982,7 @@ impl<'a> Resolver<'a> { Def::VariantCtor(_, CtorKind::Const) | Def::Const(..) if !always_binding => { // A unit struct/variant or constant pattern. - let name = ident.node.name; - self.record_use(name, ValueNS, binding.unwrap(), ident.span); + self.record_use(ident.node, ValueNS, binding.unwrap(), ident.span); Some(PathResolution::new(def)) } Def::StructCtor(..) | Def::VariantCtor(..) | @@ -2217,28 +2014,15 @@ impl<'a> Resolver<'a> { } PatKind::TupleStruct(ref path, ..) => { - self.resolve_pattern_path(pat.id, None, path, ValueNS, |def| { - match def { - Def::StructCtor(_, CtorKind::Fn) | - Def::VariantCtor(_, CtorKind::Fn) => true, - _ => false, - } - }, "tuple struct/variant"); + self.smart_resolve_path(pat.id, None, path, PathSource::TupleStruct); } PatKind::Path(ref qself, ref path) => { - self.resolve_pattern_path(pat.id, qself.as_ref(), path, ValueNS, |def| { - match def { - Def::StructCtor(_, CtorKind::Const) | - Def::VariantCtor(_, CtorKind::Const) | - Def::Const(..) | Def::AssociatedConst(..) => true, - _ => false, - } - }, "unit struct/variant or constant"); + self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat); } PatKind::Struct(ref path, ..) => { - self.resolve_struct_path(pat.id, path); + self.smart_resolve_path(pat.id, None, path, PathSource::Struct); } _ => {} @@ -2249,34 +2033,245 @@ impl<'a> Resolver<'a> { visit::walk_pat(self, pat); } - /// Handles paths that may refer to associated items - fn resolve_possibly_assoc_item(&mut self, + // High-level and context dependent path resolution routine. + // Resolves the path and records the resolution into definition map. + // If resolution fails tries several techniques to find likely + // resolution candidates, suggest imports or other help, and report + // errors in user friendly way. + fn smart_resolve_path(&mut self, + id: NodeId, + qself: Option<&QSelf>, + path: &Path, + source: PathSource) + -> PathResolution { + let segments = &path.segments.iter().map(|seg| seg.identifier).collect::<Vec<_>>(); + self.smart_resolve_path_fragment(id, qself, segments, path.span, source) + } + + fn smart_resolve_path_fragment(&mut self, id: NodeId, - maybe_qself: Option<&QSelf>, - path: &Path, - ns: Namespace) - -> Option<PathResolution> { - let ast::Path { ref segments, global, span } = *path; - let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect(); - let scope = if global { PathScope::Global } else { PathScope::Lexical }; - - if let Some(qself) = maybe_qself { + qself: Option<&QSelf>, + path: &[Ident], + span: Span, + source: PathSource) + -> PathResolution { + let ns = source.namespace(); + let is_expected = &|def| source.is_expected(def); + + // Base error is amended with one short label and possibly some longer helps/notes. + let report_errors = |this: &mut Self, def: Option<Def>| { + // Make the base error. + let expected = source.descr_expected(); + let path_str = names_to_string(path); + let code = source.error_code(def.is_some()); + let base_msg = if let Some(def) = def { + format!("expected {}, found {} `{}`", expected, def.kind_name(), path_str) + } else { + format!("unresolved {} `{}`", expected, path_str) + }; + let mut err = this.session.struct_span_err_with_code(span, &base_msg, code); + + // Emit special messages for unresolved `Self` and `self`. + if is_self_type(path, ns) { + __diagnostic_used!(E0411); + err.code("E0411".into()); + err.span_label(span, &format!("`Self` is only available in traits and impls")); + return err; + } + if is_self_value(path, ns) { + __diagnostic_used!(E0424); + err.code("E0424".into()); + err.span_label(span, &format!("`self` value is only available in \ + methods with `self` parameter")); + return err; + } + + // Try to lookup the name in more relaxed fashion for better error reporting. + let name = path.last().unwrap().name; + let candidates = this.lookup_import_candidates(name, ns, is_expected); + if !candidates.is_empty() { + // Report import candidates as help and proceed searching for labels. + show_candidates(&mut err, &candidates, def.is_some()); + } + if path.len() == 1 && this.self_type_is_available() { + if let Some(candidate) = this.lookup_assoc_candidate(name, ns, is_expected) { + let self_is_available = this.self_value_is_available(path[0].ctxt); + match candidate { + AssocSuggestion::Field => { + err.span_label(span, &format!("did you mean `self.{}`?", path_str)); + if !self_is_available { + err.span_label(span, &format!("`self` value is only available in \ + methods with `self` parameter")); + } + } + AssocSuggestion::MethodWithSelf if self_is_available => { + err.span_label(span, &format!("did you mean `self.{}(...)`?", + path_str)); + } + AssocSuggestion::MethodWithSelf | AssocSuggestion::AssocItem => { + err.span_label(span, &format!("did you mean `Self::{}`?", path_str)); + } + } + return err; + } + } + + // Try context dependent help if relaxed lookup didn't work. + if let Some(def) = def { + match (def, source) { + (Def::Macro(..), _) => { + err.span_label(span, &format!("did you mean `{}!(...)`?", path_str)); + return err; + } + (Def::TyAlias(..), PathSource::Trait) => { + err.span_label(span, &format!("type aliases cannot be used for traits")); + return err; + } + (Def::Mod(..), PathSource::Expr(Some(parent))) => match *parent { + ExprKind::Field(_, ident) => { + err.span_label(span, &format!("did you mean `{}::{}`?", + path_str, ident.node)); + return err; + } + ExprKind::MethodCall(ident, ..) => { + err.span_label(span, &format!("did you mean `{}::{}(...)`?", + path_str, ident.node)); + return err; + } + _ => {} + }, + _ if ns == ValueNS && is_struct_like(def) => { + err.span_label(span, &format!("did you mean `{} {{ /* fields */ }}`?", + path_str)); + return err; + } + _ => {} + } + } + + // Try Levenshtein if nothing else worked. + if path.len() == 1 { + if let Some(candidate) = this.lookup_typo_candidate(name, ns, is_expected) { + err.span_label(span, &format!("did you mean `{}`?", candidate)); + return err; + } + } + + // Fallback labels. + if def.is_some() { + err.span_label(span, &format!("not a {}", expected)); + } else { + err.span_label(span, &format!("no resolution found")); + } + err + }; + let report_errors = |this: &mut Self, def: Option<Def>| { + report_errors(this, def).emit(); + err_path_resolution() + }; + + let resolution = match self.resolve_qpath_anywhere(id, qself, path, ns, span, + source.defer_to_typeck(), + source.global_by_default()) { + Some(resolution) if resolution.depth == 0 => { + if is_expected(resolution.base_def) || resolution.base_def == Def::Err { + resolution + } else { + report_errors(self, Some(resolution.base_def)) + } + } + Some(resolution) if source.defer_to_typeck() => { + // Not fully resolved associated item `T::A::B` or `<T as Tr>::A::B` + // or `<T>::A::B`. If `B` should be resolved in value namespace then + // it needs to be added to the trait map. + if ns == ValueNS { + let item_name = path.last().unwrap().name; + let traits = self.get_traits_containing_item(item_name, ns); + self.trait_map.insert(id, traits); + } + resolution + } + _ => report_errors(self, None) + }; + + if let PathSource::TraitItem(..) = source {} else { + // Avoid recording definition of `A::B` in `<T as A>::B::C`. + self.record_def(id, resolution); + } + resolution + } + + fn self_type_is_available(&mut self) -> bool { + let binding = self.resolve_ident_in_lexical_scope(keywords::SelfType.ident(), TypeNS, None); + if let Some(LexicalScopeBinding::Def(def)) = binding { def != Def::Err } else { false } + } + + fn self_value_is_available(&mut self, ctxt: SyntaxContext) -> bool { + let ident = Ident { name: keywords::SelfValue.name(), ctxt: ctxt }; + let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, None); + if let Some(LexicalScopeBinding::Def(def)) = binding { def != Def::Err } else { false } + } + + // Resolve in alternative namespaces if resolution in the primary namespace fails. + fn resolve_qpath_anywhere(&mut self, + id: NodeId, + qself: Option<&QSelf>, + path: &[Ident], + primary_ns: Namespace, + span: Span, + defer_to_typeck: bool, + global_by_default: bool) + -> Option<PathResolution> { + let mut fin_res = None; + // FIXME: can't resolve paths in macro namespace yet, macros are + // processed by the little special hack below. + for (i, ns) in [primary_ns, TypeNS, ValueNS, /*MacroNS*/].iter().cloned().enumerate() { + if i == 0 || ns != primary_ns { + match self.resolve_qpath(id, qself, path, ns, span, global_by_default) { + // If defer_to_typeck, then resolution > no resolution, + // otherwise full resolution > partial resolution > no resolution. + Some(res) if res.depth == 0 || defer_to_typeck => return Some(res), + res => if fin_res.is_none() { fin_res = res }, + }; + } + } + if primary_ns != MacroNS && path.len() == 1 && + self.macro_names.contains(&path[0].name) { + // Return some dummy definition, it's enough for error reporting. + return Some(PathResolution::new(Def::Macro(DefId::local(CRATE_DEF_INDEX)))); + } + fin_res + } + + /// Handles paths that may refer to associated items. + fn resolve_qpath(&mut self, + id: NodeId, + qself: Option<&QSelf>, + path: &[Ident], + ns: Namespace, + span: Span, + global_by_default: bool) + -> Option<PathResolution> { + if let Some(qself) = qself { if qself.position == 0 { // FIXME: Create some fake resolution that can't possibly be a type. return Some(PathResolution { - base_def: Def::Mod(self.definitions.local_def_id(ast::CRATE_NODE_ID)), + base_def: Def::Mod(DefId::local(CRATE_DEF_INDEX)), depth: path.len(), }); } - // Make sure the trait is valid. - self.resolve_trait_reference(&path[..qself.position], global, None, span); + // Make sure `A::B` in `<T as A>::B::C` is a trait item. + let ns = if qself.position + 1 == path.len() { ns } else { TypeNS }; + let mut res = self.smart_resolve_path_fragment(id, None, &path[..qself.position + 1], + span, PathSource::TraitItem(ns)); + if res.base_def != Def::Err { + res.depth += path.len() - qself.position - 1; + } + return Some(res); } - let result = match self.resolve_path(&path, scope, Some(ns), Some(span)) { - PathResult::NonModule(path_res) => match path_res.base_def { - Def::Trait(..) if maybe_qself.is_some() => return None, - _ => path_res, - }, + let result = match self.resolve_path(&path, Some(ns), Some(span)) { + PathResult::NonModule(path_res) => path_res, PathResult::Module(module) if !module.is_normal() => { PathResolution::new(module.def().unwrap()) } @@ -2292,10 +2287,12 @@ impl<'a> Resolver<'a> { // // Such behavior is required for backward compatibility. // The same fallback is used when `a` resolves to nothing. - _ if self.primitive_type_table.primitive_types.contains_key(&path[0].name) => { + PathResult::Module(..) | PathResult::Failed(..) + if (ns == TypeNS || path.len() > 1) && + self.primitive_type_table.primitive_types.contains_key(&path[0].name) => { PathResolution { base_def: Def::PrimTy(self.primitive_type_table.primitive_types[&path[0].name]), - depth: segments.len() - 1, + depth: path.len() - 1, } } PathResult::Module(module) => PathResolution::new(module.def().unwrap()), @@ -2303,15 +2300,16 @@ impl<'a> Resolver<'a> { resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); err_path_resolution() } - _ => return None, + PathResult::Failed(..) => return None, + PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"), }; - if path.len() == 1 || result.base_def == Def::Err { + if path.len() == 1 || global_by_default || result.base_def == Def::Err { return Some(result); } let unqualified_result = { - match self.resolve_path(&[*path.last().unwrap()], PathScope::Lexical, Some(ns), None) { + match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) { PathResult::NonModule(path_res) => path_res.base_def, PathResult::Module(module) => module.def().unwrap(), _ => return Some(result), @@ -2327,32 +2325,24 @@ impl<'a> Resolver<'a> { fn resolve_path(&mut self, path: &[Ident], - scope: PathScope, opt_ns: Option<Namespace>, // `None` indicates a module path record_used: Option<Span>) -> PathResult<'a> { - let (mut module, allow_self) = match scope { - PathScope::Lexical => (None, true), - PathScope::Import => (Some(self.graph_root), true), - PathScope::Global => (Some(self.graph_root), false), - }; - let mut allow_super = allow_self; + let mut module = None; + let mut allow_super = true; for (i, &ident) in path.iter().enumerate() { let is_last = i == path.len() - 1; let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; - if i == 0 && allow_self && ns == TypeNS && ident.name == keywords::SelfValue.name() { - module = Some(self.module_map[&self.current_module.normal_ancestor_id.unwrap()]); - continue - } else if i == 0 && allow_self && ns == TypeNS && ident.name == "$crate" { - module = Some(self.resolve_crate_var(ident.ctxt)); + if i == 0 && ns == TypeNS && ident.name == keywords::SelfValue.name() { + module = Some(self.module_map[&self.current_module.normal_ancestor_id]); continue } else if allow_super && ns == TypeNS && ident.name == keywords::Super.name() { let current_module = if i == 0 { self.current_module } else { module.unwrap() }; - let self_module = self.module_map[¤t_module.normal_ancestor_id.unwrap()]; + let self_module = self.module_map[¤t_module.normal_ancestor_id]; if let Some(parent) = self_module.parent { - module = Some(self.module_map[&parent.normal_ancestor_id.unwrap()]); + module = Some(self.module_map[&parent.normal_ancestor_id]); continue } else { let msg = "There are too many initial `super`s.".to_string(); @@ -2361,10 +2351,18 @@ impl<'a> Resolver<'a> { } allow_super = false; + if i == 0 && ns == TypeNS && ident.name == keywords::CrateRoot.name() { + module = Some(self.graph_root); + continue + } else if i == 0 && ns == TypeNS && ident.name == "$crate" { + module = Some(self.resolve_crate_var(ident.ctxt)); + continue + } + let binding = if let Some(module) = module { - self.resolve_name_in_module(module, ident.name, ns, false, record_used) + self.resolve_ident_in_module(module, ident, ns, false, record_used) } else if opt_ns == Some(MacroNS) { - self.resolve_lexical_macro_path_segment(ident.name, ns, record_used) + self.resolve_lexical_macro_path_segment(ident, ns, record_used) } else { match self.resolve_ident_in_lexical_scope(ident, ns, record_used) { Some(LexicalScopeBinding::Item(binding)) => Ok(binding), @@ -2404,13 +2402,13 @@ impl<'a> Resolver<'a> { }); } } - let msg = if module.and_then(ModuleS::def) == self.graph_root.def() { + let msg = if module.and_then(ModuleData::def) == self.graph_root.def() { let is_mod = |def| match def { Def::Mod(..) => true, _ => false }; let mut candidates = - self.lookup_candidates(ident.name, TypeNS, is_mod).candidates; - candidates.sort_by_key(|path| (path.segments.len(), path.to_string())); + self.lookup_import_candidates(ident.name, TypeNS, is_mod); + candidates.sort_by_key(|c| (c.path.segments.len(), c.path.to_string())); if let Some(candidate) = candidates.get(0) { - format!("Did you mean `{}`?", candidate) + format!("Did you mean `{}`?", candidate.path) } else { format!("Maybe a missing `extern crate {};`?", ident) } @@ -2424,11 +2422,11 @@ impl<'a> Resolver<'a> { } } - PathResult::Module(module.unwrap()) + PathResult::Module(module.unwrap_or(self.graph_root)) } // Resolve a local definition, potentially adjusting for closures. - fn adjust_local_def(&mut self, local_def: LocalDef, span: Span) -> Def { + fn adjust_local_def(&mut self, local_def: LocalDef, record_used: Option<Span>) -> Def { let ribs = match local_def.ribs { Some((ns, i)) => &self.ribs[ns][i + 1..], None => &[] as &[_], @@ -2436,7 +2434,7 @@ impl<'a> Resolver<'a> { let mut def = local_def.def; match def { Def::Upvar(..) => { - span_bug!(span, "unexpected {:?} in bindings", def) + span_bug!(record_used.unwrap_or(DUMMY_SP), "unexpected {:?} in bindings", def) } Def::Local(def_id) => { for rib in ribs { @@ -2459,28 +2457,32 @@ impl<'a> Resolver<'a> { .entry(function_id) .or_insert_with(|| vec![]); let depth = vec.len(); - vec.push(Freevar { - def: prev_def, - span: span, - }); - def = Def::Upvar(def_id, depth, function_id); - seen.insert(node_id, depth); + + if let Some(span) = record_used { + vec.push(Freevar { + def: prev_def, + span: span, + }); + seen.insert(node_id, depth); + } } ItemRibKind | MethodRibKind(_) => { // This was an attempt to access an upvar inside a // named function item. This is not allowed, so we // report an error. - resolve_error(self, - span, - ResolutionError::CannotCaptureDynamicEnvironmentInFnItem); + if let Some(span) = record_used { + resolve_error(self, span, + ResolutionError::CannotCaptureDynamicEnvironmentInFnItem); + } return Def::Err; } ConstantItemRibKind => { // Still doesn't deal with upvars - resolve_error(self, - span, - ResolutionError::AttemptToUseNonConstantValueInConstant); + if let Some(span) = record_used { + resolve_error(self, span, + ResolutionError::AttemptToUseNonConstantValueInConstant); + } return Def::Err; } } @@ -2496,15 +2498,18 @@ impl<'a> Resolver<'a> { ItemRibKind => { // This was an attempt to use a type parameter outside // its scope. - - resolve_error(self, - span, - ResolutionError::TypeParametersFromOuterFunction); + if let Some(span) = record_used { + resolve_error(self, span, + ResolutionError::TypeParametersFromOuterFunction); + } return Def::Err; } ConstantItemRibKind => { // see #9186 - resolve_error(self, span, ResolutionError::OuterTypeParameterContext); + if let Some(span) = record_used { + resolve_error(self, span, + ResolutionError::OuterTypeParameterContext); + } return Def::Err; } } @@ -2540,7 +2545,13 @@ impl<'a> Resolver<'a> { result } - fn find_fallback_in_self_type(&mut self, name: Name) -> FallbackSuggestion { + fn lookup_assoc_candidate<FilterFn>(&mut self, + name: Name, + ns: Namespace, + filter_fn: FilterFn) + -> Option<AssocSuggestion> + where FilterFn: Fn(Def) -> bool + { fn extract_node_id(t: &Ty) -> Option<NodeId> { match t.node { TyKind::Path(None, _) => Some(t.id), @@ -2552,51 +2563,59 @@ impl<'a> Resolver<'a> { } } - if let Some(node_id) = self.current_self_type.as_ref().and_then(extract_node_id) { - // Look for a field with the same name in the current self_type. - if let Some(resolution) = self.def_map.get(&node_id) { - match resolution.base_def { - Def::Struct(did) | Def::Union(did) if resolution.depth == 0 => { - if let Some(field_names) = self.field_names.get(&did) { - if field_names.iter().any(|&field_name| name == field_name) { - return Field; + // Fields are generally expected in the same contexts as locals. + if filter_fn(Def::Local(DefId::local(CRATE_DEF_INDEX))) { + if let Some(node_id) = self.current_self_type.as_ref().and_then(extract_node_id) { + // Look for a field with the same name in the current self_type. + if let Some(resolution) = self.def_map.get(&node_id) { + match resolution.base_def { + Def::Struct(did) | Def::Union(did) if resolution.depth == 0 => { + if let Some(field_names) = self.field_names.get(&did) { + if field_names.iter().any(|&field_name| name == field_name) { + return Some(AssocSuggestion::Field); + } } } + _ => {} } - _ => {} } } } - // Look for a method in the current trait. - if let Some((trait_did, ref trait_ref)) = self.current_trait_ref { - if let Some(&is_static_method) = self.trait_item_map.get(&(name, trait_did)) { - if is_static_method { - return TraitMethod(path_names_to_string(&trait_ref.path, 0)); - } else { - return TraitItem; + // Look for associated items in the current trait. + if let Some((trait_did, _)) = self.current_trait_ref { + if let Some(&(def, has_self)) = self.trait_item_map.get(&(trait_did, name, ns)) { + if filter_fn(def) { + return Some(if has_self { + AssocSuggestion::MethodWithSelf + } else { + AssocSuggestion::AssocItem + }); } } } - NoSuggestion + None } - fn find_best_match(&mut self, name: &str) -> SuggestionType { - if let Some(macro_name) = self.macro_names.iter().find(|&n| n == &name) { - return SuggestionType::Macro(format!("{}!", macro_name)); + fn lookup_typo_candidate<FilterFn>(&mut self, + name: Name, + ns: Namespace, + filter_fn: FilterFn) + -> Option<Name> + where FilterFn: Fn(Def) -> bool + { + // FIXME: bindings in ribs provide quite modest set of candidates, + // extend it with other names in scope. + let names = self.ribs[ns].iter().rev().flat_map(|rib| { + rib.bindings.iter().filter_map(|(ident, def)| { + if filter_fn(*def) { Some(&ident.name) } else { None } + }) + }); + match find_best_match_for_name(names, &name.as_str(), None) { + Some(found) if found != name => Some(found), + _ => None, } - - let names = self.ribs[ValueNS] - .iter() - .rev() - .flat_map(|rib| rib.bindings.keys().map(|ident| &ident.name)); - - if let Some(found) = find_best_match_for_name(names, name, None) { - if found != name { - return SuggestionType::Function(found); - } - } SuggestionType::NotFound } fn resolve_labeled_block(&mut self, label: Option<SpannedIdent>, id: NodeId, block: &Block) { @@ -2611,7 +2630,7 @@ impl<'a> Resolver<'a> { } } - fn resolve_expr(&mut self, expr: &Expr, parent: Option<&Expr>) { + fn resolve_expr(&mut self, expr: &Expr, parent: Option<&ExprKind>) { // First, record candidate traits for this expression if it could // result in the invocation of a method call. @@ -2619,144 +2638,13 @@ impl<'a> Resolver<'a> { // Next, resolve the node. match expr.node { - ExprKind::Path(ref maybe_qself, ref path) => { - // This is a local path in the value namespace. Walk through - // scopes looking for it. - if let Some(path_res) = self.resolve_possibly_assoc_item(expr.id, - maybe_qself.as_ref(), path, ValueNS) { - // Check if struct variant - let is_struct_variant = match path_res.base_def { - Def::VariantCtor(_, CtorKind::Fictive) => true, - _ => false, - }; - if is_struct_variant { - let path_name = path_names_to_string(path, 0); - - let mut err = resolve_struct_error(self, - expr.span, - ResolutionError::StructVariantUsedAsFunction(&path_name)); - - let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?", - path_name); - err.help(&msg); - err.emit(); - self.record_def(expr.id, err_path_resolution()); - } else { - // Write the result into the def map. - debug!("(resolving expr) resolved `{}`", - path_names_to_string(path, 0)); - - // Partial resolutions will need the set of traits in scope, - // so they can be completed during typeck. - if path_res.depth != 0 { - let method_name = path.segments.last().unwrap().identifier.name; - let traits = self.get_traits_containing_item(method_name); - self.trait_map.insert(expr.id, traits); - } - - self.record_def(expr.id, path_res); - } - } else { - // Be helpful if the name refers to a struct - let path_name = path_names_to_string(path, 0); - let ast::Path { ref segments, global, .. } = *path; - let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect(); - let scope = if global { PathScope::Global } else { PathScope::Lexical }; - let type_res = match self.resolve_path(&path, scope, Some(TypeNS), None) { - PathResult::NonModule(type_res) => Some(type_res), - _ => None, - }; - - self.record_def(expr.id, err_path_resolution()); - - if let Some(Def::Struct(..)) = type_res.map(|r| r.base_def) { - let error_variant = - ResolutionError::StructVariantUsedAsFunction(&path_name); - let mut err = resolve_struct_error(self, expr.span, error_variant); - - let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?", - path_name); - - err.help(&msg); - err.emit(); - } else { - // Keep reporting some errors even if they're ignored above. - let mut method_scope = false; - let mut is_static = false; - self.ribs[ValueNS].iter().rev().all(|rib| { - method_scope = match rib.kind { - MethodRibKind(is_static_) => { - is_static = is_static_; - true - } - ItemRibKind | ConstantItemRibKind => false, - _ => return true, // Keep advancing - }; - false // Stop advancing - }); - - if method_scope && keywords::SelfValue.name() == &*path_name { - let error = ResolutionError::SelfNotAvailableInStaticMethod; - resolve_error(self, expr.span, error); - } else { - let fallback = - self.find_fallback_in_self_type(path.last().unwrap().name); - let (mut msg, is_field) = match fallback { - NoSuggestion => { - // limit search to 5 to reduce the number - // of stupid suggestions - (match self.find_best_match(&path_name) { - SuggestionType::Macro(s) => { - format!("the macro `{}`", s) - } - SuggestionType::Function(s) => format!("`{}`", s), - SuggestionType::NotFound => "".to_string(), - }, false) - } - Field => { - (if is_static && method_scope { - "".to_string() - } else { - format!("`self.{}`", path_name) - }, true) - } - TraitItem => (format!("to call `self.{}`", path_name), false), - TraitMethod(path_str) => - (format!("to call `{}::{}`", path_str, path_name), false), - }; - - let mut context = UnresolvedNameContext::Other; - let mut def = Def::Err; - if !msg.is_empty() { - msg = format!("did you mean {}?", msg); - } else { - // we display a help message if this is a module - if let PathResult::Module(module) = - self.resolve_path(&path, scope, None, None) { - def = module.def().unwrap(); - context = UnresolvedNameContext::PathIsMod(parent); - } - } - - let error = ResolutionError::UnresolvedName { - path: &path_name, - message: &msg, - context: context, - is_static_method: method_scope && is_static, - is_field: is_field, - def: def, - }; - resolve_error(self, expr.span, error); - } - } - } - + ExprKind::Path(ref qself, ref path) => { + self.smart_resolve_path(expr.id, qself.as_ref(), path, PathSource::Expr(parent)); visit::walk_expr(self, expr); } ExprKind::Struct(ref path, ..) => { - self.resolve_struct_path(expr.id, path); - + self.smart_resolve_path(expr.id, None, path, PathSource::Struct); visit::walk_expr(self, expr); } @@ -2819,12 +2707,13 @@ impl<'a> Resolver<'a> { self.ribs[ValueNS].pop(); } + // Equivalent to `visit::walk_expr` + passing some context to children. ExprKind::Field(ref subexpression, _) => { - self.resolve_expr(subexpression, Some(expr)); + self.resolve_expr(subexpression, Some(&expr.node)); } ExprKind::MethodCall(_, ref types, ref arguments) => { let mut arguments = arguments.iter(); - self.resolve_expr(arguments.next().unwrap(), Some(expr)); + self.resolve_expr(arguments.next().unwrap(), Some(&expr.node)); for argument in arguments { self.resolve_expr(argument, None); } @@ -2832,6 +2721,12 @@ impl<'a> Resolver<'a> { self.visit_ty(ty); } } + ExprKind::Call(ref callee, ref arguments) => { + self.resolve_expr(callee, Some(&expr.node)); + for argument in arguments { + self.resolve_expr(argument, None); + } + } _ => { visit::walk_expr(self, expr); @@ -2846,13 +2741,13 @@ impl<'a> Resolver<'a> { // field, we need to add any trait methods we find that match // the field name so that we can do some nice error reporting // later on in typeck. - let traits = self.get_traits_containing_item(name.node.name); + let traits = self.get_traits_containing_item(name.node.name, ValueNS); self.trait_map.insert(expr.id, traits); } ExprKind::MethodCall(name, ..) => { debug!("(recording candidate traits for expr) recording traits for {}", expr.id); - let traits = self.get_traits_containing_item(name.node.name); + let traits = self.get_traits_containing_item(name.node.name, ValueNS); self.trait_map.insert(expr.id, traits); } _ => { @@ -2861,75 +2756,68 @@ impl<'a> Resolver<'a> { } } - fn get_traits_containing_item(&mut self, name: Name) -> Vec<TraitCandidate> { + fn get_traits_containing_item(&mut self, name: Name, ns: Namespace) -> Vec<TraitCandidate> { debug!("(getting traits containing item) looking for '{}'", name); - fn add_trait_info(found_traits: &mut Vec<TraitCandidate>, - trait_def_id: DefId, - import_id: Option<NodeId>, - name: Name) { - debug!("(adding trait info) found trait {:?} for method '{}'", - trait_def_id, - name); - found_traits.push(TraitCandidate { - def_id: trait_def_id, - import_id: import_id, - }); - } - let mut found_traits = Vec::new(); // Look for the current trait. if let Some((trait_def_id, _)) = self.current_trait_ref { - if self.trait_item_map.contains_key(&(name, trait_def_id)) { - add_trait_info(&mut found_traits, trait_def_id, None, name); + if self.trait_item_map.contains_key(&(trait_def_id, name, ns)) { + found_traits.push(TraitCandidate { def_id: trait_def_id, import_id: None }); } } let mut search_module = self.current_module; loop { - // Look for trait children. - let mut search_in_module = |this: &mut Self, module: Module<'a>| { - let mut traits = module.traits.borrow_mut(); - if traits.is_none() { - let mut collected_traits = Vec::new(); - module.for_each_child(|name, ns, binding| { - if ns != TypeNS { return } - if let Def::Trait(_) = binding.def() { - collected_traits.push((name, binding)); - } - }); - *traits = Some(collected_traits.into_boxed_slice()); - } - - for &(trait_name, binding) in traits.as_ref().unwrap().iter() { - let trait_def_id = binding.def().def_id(); - if this.trait_item_map.contains_key(&(name, trait_def_id)) { - let mut import_id = None; - if let NameBindingKind::Import { directive, .. } = binding.kind { - let id = directive.id; - this.maybe_unused_trait_imports.insert(id); - this.add_to_glob_map(id, trait_name); - import_id = Some(id); - } - add_trait_info(&mut found_traits, trait_def_id, import_id, name); - } - } - }; - search_in_module(self, search_module); + self.get_traits_in_module_containing_item(name, ns, search_module, &mut found_traits); + match search_module.kind { + ModuleKind::Block(..) => search_module = search_module.parent.unwrap(), + _ => break, + } + } - if let ModuleKind::Block(..) = search_module.kind { - search_module = search_module.parent.unwrap(); - } else { - if !search_module.no_implicit_prelude { - self.prelude.map(|prelude| search_in_module(self, prelude)); - } - break; + if let Some(prelude) = self.prelude { + if !search_module.no_implicit_prelude { + self.get_traits_in_module_containing_item(name, ns, prelude, &mut found_traits); } } found_traits } + fn get_traits_in_module_containing_item(&mut self, + name: Name, + ns: Namespace, + module: Module, + found_traits: &mut Vec<TraitCandidate>) { + let mut traits = module.traits.borrow_mut(); + if traits.is_none() { + let mut collected_traits = Vec::new(); + module.for_each_child(|name, ns, binding| { + if ns != TypeNS { return } + if let Def::Trait(_) = binding.def() { + collected_traits.push((name, binding)); + } + }); + *traits = Some(collected_traits.into_boxed_slice()); + } + + for &(trait_name, binding) in traits.as_ref().unwrap().iter() { + let trait_def_id = binding.def().def_id(); + if self.trait_item_map.contains_key(&(trait_def_id, name, ns)) { + let import_id = match binding.kind { + NameBindingKind::Import { directive, .. } => { + self.maybe_unused_trait_imports.insert(directive.id); + self.add_to_glob_map(directive.id, trait_name); + Some(directive.id) + } + _ => None, + }; + found_traits.push(TraitCandidate { def_id: trait_def_id, import_id: import_id }); + } + } + } + /// When name resolution fails, this method can be used to look up candidate /// entities with the expected name. It allows filtering them using the /// supplied predicate (which should be used to only accept the types of @@ -2937,14 +2825,16 @@ impl<'a> Resolver<'a> { /// /// NOTE: The method does not look into imports, but this is not a problem, /// since we report the definitions (thus, the de-aliased imports). - fn lookup_candidates<FilterFn>(&mut self, - lookup_name: Name, - namespace: Namespace, - filter_fn: FilterFn) -> SuggestedCandidates - where FilterFn: Fn(Def) -> bool { - - let mut lookup_results = Vec::new(); + fn lookup_import_candidates<FilterFn>(&mut self, + lookup_name: Name, + namespace: Namespace, + filter_fn: FilterFn) + -> Vec<ImportSuggestion> + where FilterFn: Fn(Def) -> bool + { + let mut candidates = Vec::new(); let mut worklist = Vec::new(); + let mut seen_modules = FxHashSet(); worklist.push((self.graph_root, Vec::new(), false)); while let Some((in_module, @@ -2952,27 +2842,22 @@ impl<'a> Resolver<'a> { in_module_is_extern)) = worklist.pop() { self.populate_module_if_necessary(in_module); - in_module.for_each_child(|name, ns, name_binding| { + in_module.for_each_child(|ident, ns, name_binding| { // avoid imports entirely if name_binding.is_import() && !name_binding.is_extern_crate() { return; } + // avoid non-importable candidates as well + if !name_binding.is_importable() { return; } // collect results based on the filter function - if name == lookup_name && ns == namespace { + if ident.name == lookup_name && ns == namespace { if filter_fn(name_binding.def()) { // create the path - let ident = Ident::with_empty_ctxt(name); - let params = PathParameters::none(); - let segment = PathSegment { - identifier: ident, - parameters: params, - }; let span = name_binding.span; let mut segms = path_segments.clone(); - segms.push(segment); + segms.push(ident.into()); let path = Path { span: span, - global: false, segments: segms, }; // the entity is accessible in the following cases: @@ -2983,7 +2868,7 @@ impl<'a> Resolver<'a> { // declared as public (due to pruning, we don't explore // outside crate private modules => no need to check this) if !in_module_is_extern || name_binding.vis == ty::Visibility::Public { - lookup_results.push(path); + candidates.push(ImportSuggestion { path: path }); } } } @@ -2992,15 +2877,12 @@ impl<'a> Resolver<'a> { if let Some(module) = name_binding.module() { // form the path let mut path_segments = path_segments.clone(); - path_segments.push(PathSegment { - identifier: Ident::with_empty_ctxt(name), - parameters: PathParameters::none(), - }); + path_segments.push(ident.into()); if !in_module_is_extern || name_binding.vis == ty::Visibility::Public { // add the module to the lookup let is_extern = in_module_is_extern || name_binding.is_extern_crate(); - if !worklist.iter().any(|&(m, ..)| m.def() == module.def()) { + if seen_modules.insert(module.def_id().unwrap()) { worklist.push((module, path_segments, is_extern)); } } @@ -3008,82 +2890,87 @@ impl<'a> Resolver<'a> { }) } - SuggestedCandidates { - name: lookup_name.as_str().to_string(), - candidates: lookup_results, - } + candidates } fn record_def(&mut self, node_id: NodeId, resolution: PathResolution) { debug!("(recording def) recording {:?} for {}", resolution, node_id); + assert!(resolution.depth == 0 || resolution.base_def != Def::Err); if let Some(prev_res) = self.def_map.insert(node_id, resolution) { panic!("path resolved multiple times ({:?} before, {:?} now)", prev_res, resolution); } } fn resolve_visibility(&mut self, vis: &ast::Visibility) -> ty::Visibility { - let (segments, span, id) = match *vis { - ast::Visibility::Public => return ty::Visibility::Public, - ast::Visibility::Crate(_) => return ty::Visibility::Restricted(ast::CRATE_NODE_ID), - ast::Visibility::Restricted { ref path, id } => (&path.segments, path.span, id), + match *vis { + ast::Visibility::Public => ty::Visibility::Public, + ast::Visibility::Crate(..) => ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)), ast::Visibility::Inherited => { - return ty::Visibility::Restricted(self.current_module.normal_ancestor_id.unwrap()); - } - }; - - let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect(); - let mut path_resolution = err_path_resolution(); - let vis = match self.resolve_path(&path, PathScope::Import, None, Some(span)) { - PathResult::Module(module) => { - path_resolution = PathResolution::new(module.def().unwrap()); - ty::Visibility::Restricted(module.normal_ancestor_id.unwrap()) + ty::Visibility::Restricted(self.current_module.normal_ancestor_id) } - PathResult::Failed(msg, _) => { - self.session.span_err(span, &format!("failed to resolve module path. {}", msg)); - ty::Visibility::Public + ast::Visibility::Restricted { ref path, id } => { + let def = self.smart_resolve_path(id, None, path, PathSource::Visibility).base_def; + if def == Def::Err { + ty::Visibility::Public + } else { + let vis = ty::Visibility::Restricted(def.def_id()); + if self.is_accessible(vis) { + vis + } else { + self.session.span_err(path.span, "visibilities can only be restricted \ + to ancestor modules"); + ty::Visibility::Public + } + } } - _ => ty::Visibility::Public, - }; - self.def_map.insert(id, path_resolution); - if !self.is_accessible(vis) { - let msg = format!("visibilities can only be restricted to ancestor modules"); - self.session.span_err(span, &msg); } - vis } fn is_accessible(&self, vis: ty::Visibility) -> bool { - vis.is_accessible_from(self.current_module.normal_ancestor_id.unwrap(), self) + vis.is_accessible_from(self.current_module.normal_ancestor_id, self) } fn is_accessible_from(&self, vis: ty::Visibility, module: Module<'a>) -> bool { - vis.is_accessible_from(module.normal_ancestor_id.unwrap(), self) + vis.is_accessible_from(module.normal_ancestor_id, self) } fn report_errors(&mut self) { self.report_shadowing_errors(); let mut reported_spans = FxHashSet(); - for &AmbiguityError { span, name, b1, b2, lexical } in &self.ambiguity_errors { + for &AmbiguityError { span, name, b1, b2, lexical, legacy } in &self.ambiguity_errors { if !reported_spans.insert(span) { continue } let participle = |binding: &NameBinding| { if binding.is_import() { "imported" } else { "defined" } }; let msg1 = format!("`{}` could resolve to the name {} here", name, participle(b1)); let msg2 = format!("`{}` could also resolve to the name {} here", name, participle(b2)); - self.session.struct_span_err(span, &format!("`{}` is ambiguous", name)) - .span_note(b1.span, &msg1) - .span_note(b2.span, &msg2) - .note(&if !lexical && b1.is_glob_import() { - format!("consider adding an explicit import of `{}` to disambiguate", name) - } else if let Def::Macro(..) = b1.def() { - format!("macro-expanded {} do not shadow", - if b1.is_import() { "macro imports" } else { "macros" }) - } else { - format!("macro-expanded {} do not shadow when used in a macro invocation path", - if b1.is_import() { "imports" } else { "items" }) - }) - .emit(); + let note = if !lexical && b1.is_glob_import() { + format!("consider adding an explicit import of `{}` to disambiguate", name) + } else if let Def::Macro(..) = b1.def() { + format!("macro-expanded {} do not shadow", + if b1.is_import() { "macro imports" } else { "macros" }) + } else { + format!("macro-expanded {} do not shadow when used in a macro invocation path", + if b1.is_import() { "imports" } else { "items" }) + }; + if legacy { + let id = match b2.kind { + NameBindingKind::Import { directive, .. } => directive.id, + _ => unreachable!(), + }; + let mut span = MultiSpan::from_span(span); + span.push_span_label(b1.span, msg1); + span.push_span_label(b2.span, msg2); + let msg = format!("`{}` is ambiguous", name); + self.session.add_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg); + } else { + self.session.struct_span_err(span, &format!("`{}` is ambiguous", name)) + .span_note(b1.span, &msg1) + .span_note(b2.span, &msg2) + .note(¬e) + .emit(); + } } for &PrivacyError(span, name, binding) in &self.privacy_errors { @@ -3123,13 +3010,13 @@ impl<'a> Resolver<'a> { fn report_conflict(&mut self, parent: Module, - name: Name, + ident: Ident, ns: Namespace, binding: &NameBinding, old_binding: &NameBinding) { // Error on the second of two conflicting names if old_binding.span.lo > binding.span.lo { - return self.report_conflict(parent, name, ns, old_binding, binding); + return self.report_conflict(parent, ident, ns, old_binding, binding); } let container = match parent.kind { @@ -3144,7 +3031,7 @@ impl<'a> Resolver<'a> { false => ("defined", "definition"), }; - let span = binding.span; + let (name, span) = (ident.name, binding.span); if let Some(s) = self.name_already_seen.get(&name) { if s == &span { @@ -3166,40 +3053,19 @@ impl<'a> Resolver<'a> { }; let mut err = match (old_binding.is_extern_crate(), binding.is_extern_crate()) { - (true, true) => { - let mut e = struct_span_err!(self.session, span, E0259, "{}", msg); - e.span_label(span, &format!("`{}` was already imported", name)); - e - }, - (true, _) | (_, true) if binding.is_import() && old_binding.is_import() => { - let mut e = struct_span_err!(self.session, span, E0254, "{}", msg); - e.span_label(span, &"already imported"); - e - }, - (true, _) | (_, true) => { - let mut e = struct_span_err!(self.session, span, E0260, "{}", msg); - e.span_label(span, &format!("`{}` already imported", name)); - e + (true, true) => struct_span_err!(self.session, span, E0259, "{}", msg), + (true, _) | (_, true) => match binding.is_import() && old_binding.is_import() { + true => struct_span_err!(self.session, span, E0254, "{}", msg), + false => struct_span_err!(self.session, span, E0260, "{}", msg), }, _ => match (old_binding.is_import(), binding.is_import()) { - (false, false) => { - let mut e = struct_span_err!(self.session, span, E0428, "{}", msg); - e.span_label(span, &format!("already defined")); - e - }, - (true, true) => { - let mut e = struct_span_err!(self.session, span, E0252, "{}", msg); - e.span_label(span, &format!("already imported")); - e - }, - _ => { - let mut e = struct_span_err!(self.session, span, E0255, "{}", msg); - e.span_label(span, &format!("`{}` was already imported", name)); - e - } + (false, false) => struct_span_err!(self.session, span, E0428, "{}", msg), + (true, true) => struct_span_err!(self.session, span, E0252, "{}", msg), + _ => struct_span_err!(self.session, span, E0255, "{}", msg), }, }; + err.span_label(span, &format!("`{}` already {}", name, participle)); if old_binding.span != syntax_pos::DUMMY_SP { err.span_label(old_binding.span, &format!("previous {} of `{}` here", noun, name)); } @@ -3208,80 +3074,72 @@ impl<'a> Resolver<'a> { } } -fn names_to_string(names: &[Ident]) -> String { - let mut first = true; +fn is_struct_like(def: Def) -> bool { + match def { + Def::VariantCtor(_, CtorKind::Fictive) => true, + _ => PathSource::Struct.is_expected(def), + } +} + +fn is_self_type(path: &[Ident], namespace: Namespace) -> bool { + namespace == TypeNS && path.len() == 1 && path[0].name == keywords::SelfType.name() +} + +fn is_self_value(path: &[Ident], namespace: Namespace) -> bool { + namespace == ValueNS && path.len() == 1 && path[0].name == keywords::SelfValue.name() +} + +fn names_to_string(idents: &[Ident]) -> String { let mut result = String::new(); - for ident in names { - if first { - first = false - } else { - result.push_str("::") + for (i, ident) in idents.iter().filter(|i| i.name != keywords::CrateRoot.name()).enumerate() { + if i > 0 { + result.push_str("::"); } result.push_str(&ident.name.as_str()); } result } -fn path_names_to_string(path: &Path, depth: usize) -> String { - let names: Vec<_> = - path.segments[..path.segments.len() - depth].iter().map(|seg| seg.identifier).collect(); - names_to_string(&names) +fn path_names_to_string(path: &Path) -> String { + names_to_string(&path.segments.iter().map(|seg| seg.identifier).collect::<Vec<_>>()) } /// When an entity with a given name is not available in scope, we search for /// entities with that name in all crates. This method allows outputting the /// results of this search in a programmer-friendly way fn show_candidates(session: &mut DiagnosticBuilder, - candidates: &SuggestedCandidates) { - - let paths = &candidates.candidates; - - if paths.len() > 0 { - // don't show more than MAX_CANDIDATES results, so - // we're consistent with the trait suggestions - const MAX_CANDIDATES: usize = 5; - - // we want consistent results across executions, but candidates are produced - // by iterating through a hash map, so make sure they are ordered: - let mut path_strings: Vec<_> = paths.into_iter() - .map(|p| path_names_to_string(&p, 0)) - .collect(); - path_strings.sort(); - - // behave differently based on how many candidates we have: - if !paths.is_empty() { - if paths.len() == 1 { - session.help( - &format!("you can import it into scope: `use {};`.", - &path_strings[0]), - ); - } else { - session.help("you can import several candidates \ - into scope (`use ...;`):"); - let count = path_strings.len() as isize - MAX_CANDIDATES as isize + 1; - - for (idx, path_string) in path_strings.iter().enumerate() { - if idx == MAX_CANDIDATES - 1 && count > 1 { - session.help( - &format!(" and {} other candidates", count).to_string(), - ); - break; - } else { - session.help( - &format!(" `{}`", path_string).to_string(), - ); - } - } - } - } - } else { - // nothing found: - session.help( - &format!("no candidates by the name of `{}` found in your \ - project; maybe you misspelled the name or forgot to import \ - an external crate?", candidates.name.to_string()), - ); + candidates: &[ImportSuggestion], + better: bool) { + // don't show more than MAX_CANDIDATES results, so + // we're consistent with the trait suggestions + const MAX_CANDIDATES: usize = 5; + + // we want consistent results across executions, but candidates are produced + // by iterating through a hash map, so make sure they are ordered: + let mut path_strings: Vec<_> = + candidates.into_iter().map(|c| path_names_to_string(&c.path)).collect(); + path_strings.sort(); + + let better = if better { "better " } else { "" }; + let msg_diff = match path_strings.len() { + 1 => " is found in another module, you can import it", + _ => "s are found in other modules, you can import them", }; + session.help(&format!("possible {}candidate{} into scope:", better, msg_diff)); + + let count = path_strings.len() as isize - MAX_CANDIDATES as isize + 1; + for (idx, path_string) in path_strings.iter().enumerate() { + if idx == MAX_CANDIDATES - 1 && count > 1 { + session.help( + &format!(" and {} other candidates", count).to_string(), + ); + break; + } else { + session.help( + &format!(" `use {};`", path_string).to_string(), + ); + } + } } /// A somewhat inefficient routine to obtain the name of a module. diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 6c02967672d..44cc580ad12 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -9,7 +9,7 @@ // except according to those terms. use {AmbiguityError, Resolver, ResolutionError, resolve_error}; -use {Module, ModuleKind, NameBinding, NameBindingKind, PathScope, PathResult}; +use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult}; use Namespace::{self, MacroNS}; use build_reduced_graph::BuildReducedGraphVisitor; use resolve_imports::ImportResolver; @@ -19,16 +19,18 @@ use rustc::hir::map::{self, DefCollector}; use rustc::ty; use std::cell::Cell; use std::rc::Rc; -use syntax::ast::{self, Name}; +use syntax::ast::{self, Name, Ident}; +use syntax::attr; use syntax::errors::DiagnosticBuilder; use syntax::ext::base::{self, Determinacy, MultiModifier, MultiDecorator}; -use syntax::ext::base::{NormalTT, SyntaxExtension}; -use syntax::ext::expand::Expansion; +use syntax::ext::base::{NormalTT, Resolver as SyntaxResolver, SyntaxExtension}; +use syntax::ext::expand::{Expansion, mark_tts}; use syntax::ext::hygiene::Mark; use syntax::ext::tt::macro_rules; use syntax::feature_gate::{emit_feature_err, GateIssue}; use syntax::fold::Folder; use syntax::ptr::P; +use syntax::symbol::keywords; use syntax::util::lev_distance::find_best_match_for_name; use syntax::visit::Visitor; use syntax_pos::{Span, DUMMY_SP}; @@ -86,7 +88,7 @@ impl<'a> base::Resolver for Resolver<'a> { fn get_module_scope(&mut self, id: ast::NodeId) -> Mark { let mark = Mark::fresh(); - let module = self.module_map[&id]; + let module = self.module_map[&self.definitions.local_def_id(id)]; self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData { module: Cell::new(module), def_index: module.def_id().unwrap().index, @@ -104,15 +106,13 @@ impl<'a> base::Resolver for Resolver<'a> { fn fold_path(&mut self, mut path: ast::Path) -> ast::Path { let ident = path.segments[0].identifier; if ident.name == "$crate" { - path.global = true; + path.segments[0].identifier.name = keywords::CrateRoot.name(); let module = self.0.resolve_crate_var(ident.ctxt); - if module.is_local() { - path.segments.remove(0); - } else { - path.segments[0].identifier = match module.kind { - ModuleKind::Def(_, name) => ast::Ident::with_empty_ctxt(name), + if !module.is_local() { + path.segments.insert(1, match module.kind { + ModuleKind::Def(_, name) => ast::Ident::with_empty_ctxt(name).into(), _ => unreachable!(), - }; + }) } } path @@ -122,6 +122,10 @@ impl<'a> base::Resolver for Resolver<'a> { EliminateCrateVar(self).fold_item(item).expect_one("") } + fn is_whitelisted_legacy_custom_derive(&self, name: Name) -> bool { + self.whitelisted_legacy_custom_derives.contains(&name) + } + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) { let invocation = self.invocations[&mark]; self.collect_def_ids(invocation, expansion); @@ -138,34 +142,6 @@ impl<'a> base::Resolver for Resolver<'a> { invocation.expansion.set(visitor.legacy_scope); } - fn add_macro(&mut self, scope: Mark, mut def: ast::MacroDef, export: bool) { - if def.ident.name == "macro_rules" { - self.session.span_err(def.span, "user-defined macros may not be named `macro_rules`"); - } - - let invocation = self.invocations[&scope]; - let binding = self.arenas.alloc_legacy_binding(LegacyBinding { - parent: Cell::new(invocation.legacy_scope.get()), - name: def.ident.name, - ext: Rc::new(macro_rules::compile(&self.session.parse_sess, &def)), - span: def.span, - }); - invocation.legacy_scope.set(LegacyScope::Binding(binding)); - self.macro_names.insert(def.ident.name); - - if export { - def.id = self.next_node_id(); - DefCollector::new(&mut self.definitions).with_parent(CRATE_DEF_INDEX, |collector| { - collector.visit_macro_def(&def) - }); - self.macro_exports.push(Export { - name: def.ident.name, - def: Def::Macro(self.definitions.local_def_id(def.id)), - }); - self.exported_macros.push(def); - } - } - fn add_ext(&mut self, ident: ast::Ident, ext: Rc<SyntaxExtension>) { if let NormalTT(..) = *ext { self.macro_names.insert(ident.name); @@ -178,7 +154,7 @@ impl<'a> base::Resolver for Resolver<'a> { let binding = self.arenas.alloc_name_binding(NameBinding { kind: NameBindingKind::Def(Def::Macro(def_id)), span: DUMMY_SP, - vis: ty::Visibility::PrivateExternal, + vis: ty::Visibility::Invisible, expansion: Mark::root(), }); self.builtin_macros.insert(ident.name, binding); @@ -209,21 +185,20 @@ impl<'a> base::Resolver for Resolver<'a> { fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool) -> Result<Rc<SyntaxExtension>, Determinacy> { - let ast::Path { ref segments, global, span } = *path; - if segments.iter().any(|segment| !segment.parameters.is_empty()) { + let ast::Path { ref segments, span } = *path; + if segments.iter().any(|segment| segment.parameters.is_some()) { let kind = - if segments.last().unwrap().parameters.is_empty() { "module" } else { "macro" }; + if segments.last().unwrap().parameters.is_some() { "macro" } else { "module" }; let msg = format!("type parameters are not allowed on {}s", kind); self.session.span_err(path.span, &msg); return Err(Determinacy::Determined); } - let path_scope = if global { PathScope::Global } else { PathScope::Lexical }; let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect(); let invocation = self.invocations[&scope]; self.current_module = invocation.module.get(); - if path.len() > 1 || global { + if path.len() > 1 { if !self.use_extern_macros { let msg = "non-ident macro paths are experimental"; let feature = "use_extern_macros"; @@ -231,14 +206,17 @@ impl<'a> base::Resolver for Resolver<'a> { return Err(Determinacy::Determined); } - let ext = match self.resolve_path(&path, path_scope, Some(MacroNS), None) { - PathResult::NonModule(path_res) => Ok(self.get_macro(path_res.base_def)), + let ext = match self.resolve_path(&path, Some(MacroNS), None) { + PathResult::NonModule(path_res) => match path_res.base_def { + Def::Err => Err(Determinacy::Determined), + def @ _ => Ok(self.get_macro(def)), + }, PathResult::Module(..) => unreachable!(), PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined), _ => Err(Determinacy::Determined), }; self.current_module.macro_resolutions.borrow_mut() - .push((path.into_boxed_slice(), path_scope, span)); + .push((path.into_boxed_slice(), span)); return ext; } @@ -246,7 +224,7 @@ impl<'a> base::Resolver for Resolver<'a> { let result = match self.resolve_legacy_scope(&invocation.legacy_scope, name, false) { Some(MacroBinding::Legacy(binding)) => Ok(binding.ext.clone()), Some(MacroBinding::Modern(binding)) => Ok(binding.get_macro(self)), - None => match self.resolve_lexical_macro_path_segment(name, MacroNS, None) { + None => match self.resolve_lexical_macro_path_segment(path[0], MacroNS, None) { Ok(binding) => Ok(binding.get_macro(self)), Err(Determinacy::Undetermined) if !force => return Err(Determinacy::Undetermined), _ => { @@ -260,7 +238,7 @@ impl<'a> base::Resolver for Resolver<'a> { }; if self.use_extern_macros { - self.current_module.legacy_macro_resolutions.borrow_mut().push((scope, name, span)); + self.current_module.legacy_macro_resolutions.borrow_mut().push((scope, path[0], span)); } result } @@ -269,7 +247,7 @@ impl<'a> base::Resolver for Resolver<'a> { impl<'a> Resolver<'a> { // Resolve the initial segment of a non-global macro path (e.g. `foo` in `foo::bar!();`) pub fn resolve_lexical_macro_path_segment(&mut self, - name: Name, + ident: Ident, ns: Namespace, record_used: Option<Span>) -> Result<&'a NameBinding<'a>, Determinacy> { @@ -278,7 +256,7 @@ impl<'a> Resolver<'a> { loop { // Since expanded macros may not shadow the lexical scope (enforced below), // we can ignore unresolved invocations (indicated by the penultimate argument). - match self.resolve_name_in_module(module, name, ns, true, record_used) { + match self.resolve_ident_in_module(module, ident, ns, true, record_used) { Ok(binding) => { let span = match record_used { Some(span) => span, @@ -286,8 +264,10 @@ impl<'a> Resolver<'a> { }; match potential_expanded_shadower { Some(shadower) if shadower.def() != binding.def() => { + let name = ident.name; self.ambiguity_errors.push(AmbiguityError { span: span, name: name, b1: shadower, b2: binding, lexical: true, + legacy: false, }); return Ok(shadower); } @@ -373,8 +353,8 @@ impl<'a> Resolver<'a> { pub fn finalize_current_module_macro_resolutions(&mut self) { let module = self.current_module; - for &(ref path, scope, span) in module.macro_resolutions.borrow().iter() { - match self.resolve_path(path, scope, Some(MacroNS), Some(span)) { + for &(ref path, span) in module.macro_resolutions.borrow().iter() { + match self.resolve_path(path, Some(MacroNS), Some(span)) { PathResult::NonModule(_) => {}, PathResult::Failed(msg, _) => { resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); @@ -383,10 +363,10 @@ impl<'a> Resolver<'a> { } } - for &(mark, name, span) in module.legacy_macro_resolutions.borrow().iter() { + for &(mark, ident, span) in module.legacy_macro_resolutions.borrow().iter() { let legacy_scope = &self.invocations[&mark].legacy_scope; - let legacy_resolution = self.resolve_legacy_scope(legacy_scope, name, true); - let resolution = self.resolve_lexical_macro_path_segment(name, MacroNS, Some(span)); + let legacy_resolution = self.resolve_legacy_scope(legacy_scope, ident.name, true); + let resolution = self.resolve_lexical_macro_path_segment(ident, MacroNS, Some(span)); let (legacy_resolution, resolution) = match (legacy_resolution, resolution) { (Some(legacy_resolution), Ok(resolution)) => (legacy_resolution, resolution), _ => continue, @@ -396,9 +376,9 @@ impl<'a> Resolver<'a> { MacroBinding::Modern(binding) => (binding.span, "imported"), MacroBinding::Legacy(binding) => (binding.span, "defined"), }; - let msg1 = format!("`{}` could resolve to the macro {} here", name, participle); - let msg2 = format!("`{}` could also resolve to the macro imported here", name); - self.session.struct_span_err(span, &format!("`{}` is ambiguous", name)) + let msg1 = format!("`{}` could resolve to the macro {} here", ident, participle); + let msg2 = format!("`{}` could also resolve to the macro imported here", ident); + self.session.struct_span_err(span, &format!("`{}` is ambiguous", ident)) .span_note(legacy_span, &msg1) .span_note(resolution.span, &msg2) .emit(); @@ -442,4 +422,47 @@ impl<'a> Resolver<'a> { expansion.visit_with(def_collector) }); } + + pub fn define_macro(&mut self, item: &ast::Item, legacy_scope: &mut LegacyScope<'a>) { + let tts = match item.node { + ast::ItemKind::Mac(ref mac) => &mac.node.tts, + _ => unreachable!(), + }; + + if item.ident.name == "macro_rules" { + self.session.span_err(item.span, "user-defined macros may not be named `macro_rules`"); + } + + let mark = Mark::from_placeholder_id(item.id); + let invocation = self.invocations[&mark]; + invocation.module.set(self.current_module); + + let mut def = ast::MacroDef { + ident: item.ident, + attrs: item.attrs.clone(), + id: ast::DUMMY_NODE_ID, + span: item.span, + body: mark_tts(tts, mark), + }; + + *legacy_scope = LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding { + parent: Cell::new(*legacy_scope), + name: def.ident.name, + ext: Rc::new(macro_rules::compile(&self.session.parse_sess, &def)), + span: def.span, + })); + self.macro_names.insert(def.ident.name); + + if attr::contains_name(&def.attrs, "macro_export") { + def.id = self.next_node_id(); + DefCollector::new(&mut self.definitions).with_parent(CRATE_DEF_INDEX, |collector| { + collector.visit_macro_def(&def) + }); + self.macro_exports.push(Export { + name: def.ident.name, + def: Def::Macro(self.definitions.local_def_id(def.id)), + }); + self.exported_macros.push(def); + } + } } diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index b634d57a842..41d8f16b88d 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -12,18 +12,20 @@ use self::ImportDirectiveSubclass::*; use {AmbiguityError, Module, PerNS}; use Namespace::{self, TypeNS, MacroNS}; -use {NameBinding, NameBindingKind, PathResult, PathScope, PrivacyError, ToNameBinding}; +use {NameBinding, NameBindingKind, PathResult, PrivacyError}; use Resolver; use {names_to_string, module_to_string}; use {resolve_error, ResolutionError}; use rustc::ty; use rustc::lint::builtin::PRIVATE_IN_PUBLIC; +use rustc::hir::def_id::DefId; use rustc::hir::def::*; -use syntax::ast::{Ident, NodeId, Name}; +use syntax::ast::{Ident, NodeId}; use syntax::ext::base::Determinacy::{self, Determined, Undetermined}; use syntax::ext::hygiene::Mark; +use syntax::parse::token; use syntax::symbol::keywords; use syntax::util::lev_distance::find_best_match_for_name; use syntax_pos::Span; @@ -35,8 +37,8 @@ use std::mem; #[derive(Clone, Debug)] pub enum ImportDirectiveSubclass<'a> { SingleImport { - target: Name, - source: Name, + target: Ident, + source: Ident, result: PerNS<Cell<Result<&'a NameBinding<'a>, Determinacy>>>, }, GlobImport { @@ -126,33 +128,32 @@ impl<'a> NameResolution<'a> { } impl<'a> Resolver<'a> { - fn resolution(&self, module: Module<'a>, name: Name, ns: Namespace) + fn resolution(&self, module: Module<'a>, ident: Ident, ns: Namespace) -> &'a RefCell<NameResolution<'a>> { - *module.resolutions.borrow_mut().entry((name, ns)) + let ident = ident.unhygienize(); + *module.resolutions.borrow_mut().entry((ident, ns)) .or_insert_with(|| self.arenas.alloc_name_resolution()) } - /// Attempts to resolve the supplied name in the given module for the given namespace. - /// If successful, returns the binding corresponding to the name. + /// Attempts to resolve `ident` in namespaces `ns` of `module`. /// Invariant: if `record_used` is `Some`, import resolution must be complete. - pub fn resolve_name_in_module(&mut self, - module: Module<'a>, - name: Name, - ns: Namespace, - ignore_unresolved_invocations: bool, - record_used: Option<Span>) - -> Result<&'a NameBinding<'a>, Determinacy> { + pub fn resolve_ident_in_module(&mut self, + module: Module<'a>, + ident: Ident, + ns: Namespace, + ignore_unresolved_invocations: bool, + record_used: Option<Span>) + -> Result<&'a NameBinding<'a>, Determinacy> { self.populate_module_if_necessary(module); - let resolution = self.resolution(module, name, ns); - let resolution = match resolution.borrow_state() { - ::std::cell::BorrowState::Unused => resolution.borrow_mut(), - _ => return Err(Determined), // This happens when there is a cycle of imports - }; + let resolution = self.resolution(module, ident, ns) + .try_borrow_mut() + .map_err(|_| Determined)?; // This happens when there is a cycle of imports if let Some(span) = record_used { if let Some(binding) = resolution.binding { if let Some(shadowed_glob) = resolution.shadows_glob { + let name = ident.name; // If we ignore unresolved invocations, we must forbid // expanded shadowing to avoid time travel. if ignore_unresolved_invocations && @@ -161,14 +162,15 @@ impl<'a> Resolver<'a> { binding.def() != shadowed_glob.def() { self.ambiguity_errors.push(AmbiguityError { span: span, name: name, lexical: false, b1: binding, b2: shadowed_glob, + legacy: false, }); } } - if self.record_use(name, ns, binding, span) { + if self.record_use(ident, ns, binding, span) { return Ok(self.dummy_binding); } if !self.is_accessible(binding.vis) { - self.privacy_errors.push(PrivacyError(span, name, binding)); + self.privacy_errors.push(PrivacyError(span, ident.name, binding)); } } @@ -196,11 +198,11 @@ impl<'a> Resolver<'a> { Some(module) => module, None => return Err(Undetermined), }; - let name = match directive.subclass { + let ident = match directive.subclass { SingleImport { source, .. } => source, _ => unreachable!(), }; - match self.resolve_name_in_module(module, name, ns, false, None) { + match self.resolve_ident_in_module(module, ident, ns, false, None) { Err(Determined) => {} _ => return Err(Undetermined), } @@ -222,7 +224,7 @@ impl<'a> Resolver<'a> { for directive in module.globs.borrow().iter() { if self.is_accessible(directive.vis.get()) { if let Some(module) = directive.imported_module.get() { - let result = self.resolve_name_in_module(module, name, ns, false, None); + let result = self.resolve_ident_in_module(module, ident, ns, false, None); if let Err(Undetermined) = result { return Err(Undetermined); } @@ -273,8 +275,8 @@ impl<'a> Resolver<'a> { // Given a binding and an import directive that resolves to it, // return the corresponding binding defined by the import directive. - pub fn import(&mut self, binding: &'a NameBinding<'a>, directive: &'a ImportDirective<'a>) - -> NameBinding<'a> { + pub fn import(&self, binding: &'a NameBinding<'a>, directive: &'a ImportDirective<'a>) + -> &'a NameBinding<'a> { let vis = if binding.pseudo_vis().is_at_least(directive.vis.get(), self) || !directive.is_glob() && binding.is_extern_crate() { // c.f. `PRIVATE_IN_PUBLIC` directive.vis.get() @@ -288,7 +290,7 @@ impl<'a> Resolver<'a> { } } - NameBinding { + self.arenas.alloc_name_binding(NameBinding { kind: NameBindingKind::Import { binding: binding, directive: directive, @@ -297,16 +299,17 @@ impl<'a> Resolver<'a> { span: directive.span, vis: vis, expansion: directive.expansion, - } + }) } // Define the name or return the existing binding if there is a collision. - pub fn try_define<T>(&mut self, module: Module<'a>, name: Name, ns: Namespace, binding: T) - -> Result<(), &'a NameBinding<'a>> - where T: ToNameBinding<'a> - { - let binding = self.arenas.alloc_name_binding(binding.to_name_binding()); - self.update_resolution(module, name, ns, |this, resolution| { + pub fn try_define(&mut self, + module: Module<'a>, + ident: Ident, + ns: Namespace, + binding: &'a NameBinding<'a>) + -> Result<(), &'a NameBinding<'a>> { + self.update_resolution(module, ident, ns, |this, resolution| { if let Some(old_binding) = resolution.binding { if binding.is_glob_import() { if !old_binding.is_glob_import() && @@ -314,7 +317,7 @@ impl<'a> Resolver<'a> { resolution.shadows_glob = Some(binding); } else if binding.def() != old_binding.def() { resolution.binding = Some(this.ambiguity(old_binding, binding)); - } else if !old_binding.vis.is_at_least(binding.vis, this) { + } else if !old_binding.vis.is_at_least(binding.vis, &*this) { // We are glob-importing the same item but with greater visibility. resolution.binding = Some(binding); } @@ -337,10 +340,10 @@ impl<'a> Resolver<'a> { }) } - pub fn ambiguity(&mut self, b1: &'a NameBinding<'a>, b2: &'a NameBinding<'a>) - -> &'a NameBinding<'a> { + pub fn ambiguity(&self, b1: &'a NameBinding<'a>, b2: &'a NameBinding<'a>) + -> &'a NameBinding<'a> { self.arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Ambiguity { b1: b1, b2: b2 }, + kind: NameBindingKind::Ambiguity { b1: b1, b2: b2, legacy: false }, vis: if b1.vis.is_at_least(b2.vis, self) { b1.vis } else { b2.vis }, span: b1.span, expansion: Mark::root(), @@ -349,13 +352,14 @@ impl<'a> Resolver<'a> { // Use `f` to mutate the resolution of the name in the module. // If the resolution becomes a success, define it in the module's glob importers. - fn update_resolution<T, F>(&mut self, module: Module<'a>, name: Name, ns: Namespace, f: F) -> T + fn update_resolution<T, F>(&mut self, module: Module<'a>, ident: Ident, ns: Namespace, f: F) + -> T where F: FnOnce(&mut Resolver<'a>, &mut NameResolution<'a>) -> T { // Ensure that `resolution` isn't borrowed when defining in the module's glob importers, // during which the resolution might end up getting re-defined via a glob cycle. let (binding, t) = { - let mut resolution = &mut *self.resolution(module, name, ns).borrow_mut(); + let mut resolution = &mut *self.resolution(module, ident, ns).borrow_mut(); let old_binding = resolution.binding(); let t = f(self, resolution); @@ -374,7 +378,7 @@ impl<'a> Resolver<'a> { for directive in module.glob_importers.borrow_mut().iter() { if self.is_accessible_from(binding.vis, directive.parent) { let imported_binding = self.import(binding, directive); - let _ = self.try_define(directive.parent, name, ns, imported_binding); + let _ = self.try_define(directive.parent, ident, ns, imported_binding); } } @@ -388,7 +392,7 @@ impl<'a> Resolver<'a> { let dummy_binding = self.dummy_binding; let dummy_binding = self.import(dummy_binding, directive); self.per_ns(|this, ns| { - let _ = this.try_define(directive.parent, target, ns, dummy_binding.clone()); + let _ = this.try_define(directive.parent, target, ns, dummy_binding); }); } } @@ -411,9 +415,9 @@ impl<'a, 'b: 'a> ::std::ops::DerefMut for ImportResolver<'a, 'b> { } } -impl<'a, 'b: 'a> ty::NodeIdTree for ImportResolver<'a, 'b> { - fn is_descendant_of(&self, node: NodeId, ancestor: NodeId) -> bool { - self.resolver.is_descendant_of(node, ancestor) +impl<'a, 'b: 'a> ty::DefIdTree for &'a ImportResolver<'a, 'b> { + fn parent(self, id: DefId) -> Option<DefId> { + self.resolver.parent(id) } } @@ -486,8 +490,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { let vis = directive.vis.get(); // For better failure detection, pretend that the import will not define any names // while resolving its module path. - directive.vis.set(ty::Visibility::PrivateExternal); - let result = self.resolve_path(&directive.module_path, PathScope::Import, None, None); + directive.vis.set(ty::Visibility::Invisible); + let result = self.resolve_path(&directive.module_path, None, None); directive.vis.set(vis); match result { @@ -510,15 +514,16 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { let mut indeterminate = false; self.per_ns(|this, ns| { if let Err(Undetermined) = result[ns].get() { - result[ns].set(this.resolve_name_in_module(module, source, ns, false, None)); + result[ns].set(this.resolve_ident_in_module(module, source, ns, false, None)); } else { return }; + let parent = directive.parent; match result[ns].get() { Err(Undetermined) => indeterminate = true, Err(Determined) => { - this.update_resolution(directive.parent, target, ns, |_, resolution| { + this.update_resolution(parent, target, ns, |_, resolution| { resolution.single_imports.directive_failed() }); } @@ -533,10 +538,9 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { } Ok(binding) => { let imported_binding = this.import(binding, directive); - let conflict = this.try_define(directive.parent, target, ns, imported_binding); + let conflict = this.try_define(parent, target, ns, imported_binding); if let Err(old_binding) = conflict { - let binding = &this.import(binding, directive); - this.report_conflict(directive.parent, target, ns, binding, old_binding); + this.report_conflict(parent, target, ns, imported_binding, old_binding); } } } @@ -550,15 +554,17 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { self.current_module = directive.parent; let ImportDirective { ref module_path, span, .. } = *directive; - let module_result = self.resolve_path(&module_path, PathScope::Import, None, Some(span)); + let module_result = self.resolve_path(&module_path, None, Some(span)); let module = match module_result { PathResult::Module(module) => module, PathResult::Failed(msg, _) => { - let mut path = vec![keywords::SelfValue.ident()]; - path.extend(module_path); - let result = self.resolve_path(&path, PathScope::Import, None, None); - return if let PathResult::Module(..) = result { - Some(format!("Did you mean `self::{}`?", &names_to_string(module_path))) + let (mut self_path, mut self_result) = (module_path.clone(), None); + if !self_path.is_empty() && !token::Ident(self_path[0]).is_path_segment_keyword() { + self_path[0].name = keywords::SelfValue.name(); + self_result = Some(self.resolve_path(&self_path, None, None)); + } + return if let Some(PathResult::Module(..)) = self_result { + Some(format!("Did you mean `{}`?", names_to_string(&self_path))) } else { Some(msg) }; @@ -566,7 +572,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { _ => return None, }; - let (name, result) = match directive.subclass { + let (ident, result) = match directive.subclass { SingleImport { source, ref result, .. } => (source, result), GlobImport { .. } if module.def_id() == directive.parent.def_id() => { // Importing a module into itself is not allowed. @@ -574,8 +580,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { } GlobImport { is_prelude, ref max_vis } => { if !is_prelude && - max_vis.get() != ty::Visibility::PrivateExternal && // Allow empty globs. - !max_vis.get().is_at_least(directive.vis.get(), self) { + max_vis.get() != ty::Visibility::Invisible && // Allow empty globs. + !max_vis.get().is_at_least(directive.vis.get(), &*self) { let msg = "A non-empty glob must import something with the glob's visibility"; self.session.span_err(directive.span, msg); } @@ -588,8 +594,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { self.per_ns(|this, ns| { if let Ok(binding) = result[ns].get() { all_ns_err = false; - if this.record_use(name, ns, binding, directive.span) { - this.resolution(module, name, ns).borrow_mut().binding = + if this.record_use(ident, ns, binding, directive.span) { + this.resolution(module, ident, ns).borrow_mut().binding = Some(this.dummy_binding); } } @@ -598,7 +604,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { if all_ns_err { let mut all_ns_failed = true; self.per_ns(|this, ns| { - match this.resolve_name_in_module(module, name, ns, false, Some(span)) { + match this.resolve_ident_in_module(module, ident, ns, false, Some(span)) { Ok(_) => all_ns_failed = false, _ => {} } @@ -606,27 +612,28 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { return if all_ns_failed { let resolutions = module.resolutions.borrow(); - let names = resolutions.iter().filter_map(|(&(ref n, _), resolution)| { - if *n == name { return None; } // Never suggest the same name + let names = resolutions.iter().filter_map(|(&(ref i, _), resolution)| { + if *i == ident { return None; } // Never suggest the same name match *resolution.borrow() { - NameResolution { binding: Some(_), .. } => Some(n), + NameResolution { binding: Some(_), .. } => Some(&i.name), NameResolution { single_imports: SingleImports::None, .. } => None, - _ => Some(n), + _ => Some(&i.name), } }); - let lev_suggestion = match find_best_match_for_name(names, &name.as_str(), None) { - Some(name) => format!(". Did you mean to use `{}`?", name), - None => "".to_owned(), - }; + let lev_suggestion = + match find_best_match_for_name(names, &ident.name.as_str(), None) { + Some(name) => format!(". Did you mean to use `{}`?", name), + None => "".to_owned(), + }; let module_str = module_to_string(module); let msg = if &module_str == "???" { - format!("no `{}` in the root{}", name, lev_suggestion) + format!("no `{}` in the root{}", ident, lev_suggestion) } else { - format!("no `{}` in `{}`{}", name, module_str, lev_suggestion) + format!("no `{}` in `{}`{}", ident, module_str, lev_suggestion) }; Some(msg) } else { - // `resolve_name_in_module` reported a privacy error. + // `resolve_ident_in_module` reported a privacy error. self.import_dummy_binding(directive); None } @@ -637,7 +644,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { self.per_ns(|this, ns| { if let Ok(binding) = result[ns].get() { let vis = directive.vis.get(); - if !binding.pseudo_vis().is_at_least(vis, this) { + if !binding.pseudo_vis().is_at_least(vis, &*this) { reexport_error = Some((ns, binding)); } else { any_successful_reexport = true; @@ -651,18 +658,18 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { if ns == TypeNS && binding.is_extern_crate() { let msg = format!("extern crate `{}` is private, and cannot be reexported \ (error E0364), consider declaring with `pub`", - name); + ident); self.session.add_lint(PRIVATE_IN_PUBLIC, directive.id, directive.span, msg); } else if ns == TypeNS { struct_span_err!(self.session, directive.span, E0365, - "`{}` is private, and cannot be reexported", name) - .span_label(directive.span, &format!("reexport of private `{}`", name)) - .note(&format!("consider declaring type or module `{}` with `pub`", name)) + "`{}` is private, and cannot be reexported", ident) + .span_label(directive.span, &format!("reexport of private `{}`", ident)) + .note(&format!("consider declaring type or module `{}` with `pub`", ident)) .emit(); } else { - let msg = format!("`{}` is private, and cannot be reexported", name); + let msg = format!("`{}` is private, and cannot be reexported", ident); let note_msg = - format!("consider marking `{}` as `pub` in the imported module", name); + format!("consider marking `{}` as `pub` in the imported module", ident); struct_span_err!(self.session, directive.span, E0364, "{}", &msg) .span_note(directive.span, ¬e_msg) .emit(); @@ -699,21 +706,18 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { // Ensure that `resolutions` isn't borrowed during `try_define`, // since it might get updated via a glob cycle. - let bindings = module.resolutions.borrow().iter().filter_map(|(name, resolution)| { - resolution.borrow().binding().map(|binding| (*name, binding)) + let bindings = module.resolutions.borrow().iter().filter_map(|(&ident, resolution)| { + resolution.borrow().binding().map(|binding| (ident, binding)) }).collect::<Vec<_>>(); - for ((name, ns), binding) in bindings { + for ((ident, ns), binding) in bindings { if binding.pseudo_vis() == ty::Visibility::Public || self.is_accessible(binding.vis) { let imported_binding = self.import(binding, directive); - let _ = self.try_define(directive.parent, name, ns, imported_binding); + let _ = self.try_define(directive.parent, ident, ns, imported_binding); } } // Record the destination of this import - if let Some(did) = module.def_id() { - let resolution = PathResolution::new(Def::Mod(did)); - self.def_map.insert(directive.id, resolution); - } + self.record_def(directive.id, PathResolution::new(module.def().unwrap())); } // Miscellaneous post-processing, including recording reexports, reporting conflicts, @@ -727,8 +731,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { reexports = mem::replace(&mut self.macro_exports, Vec::new()); } - for (&(name, ns), resolution) in module.resolutions.borrow().iter() { - let resolution = resolution.borrow(); + for (&(ident, ns), resolution) in module.resolutions.borrow().iter() { + let resolution = &mut *resolution.borrow_mut(); let binding = match resolution.binding { Some(binding) => binding, None => continue, @@ -741,18 +745,38 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { if !def.def_id().is_local() { self.session.cstore.export_macros(def.def_id().krate); } - reexports.push(Export { name: name, def: def }); + reexports.push(Export { name: ident.name, def: def }); } } - if let NameBindingKind::Import { binding: orig_binding, directive, .. } = binding.kind { - if ns == TypeNS && orig_binding.is_variant() && - !orig_binding.vis.is_at_least(binding.vis, self) { - let msg = format!("variant `{}` is private, and cannot be reexported \ - (error E0364), consider declaring its enum as `pub`", - name); - self.session.add_lint(PRIVATE_IN_PUBLIC, directive.id, binding.span, msg); + match binding.kind { + NameBindingKind::Import { binding: orig_binding, directive, .. } => { + if ns == TypeNS && orig_binding.is_variant() && + !orig_binding.vis.is_at_least(binding.vis, &*self) { + let msg = format!("variant `{}` is private, and cannot be reexported \ + (error E0364), consider declaring its enum as `pub`", + ident); + self.session.add_lint(PRIVATE_IN_PUBLIC, directive.id, binding.span, msg); + } + } + NameBindingKind::Ambiguity { b1, b2, .. } + if b1.is_glob_import() && b2.is_glob_import() => { + let (orig_b1, orig_b2) = match (&b1.kind, &b2.kind) { + (&NameBindingKind::Import { binding: b1, .. }, + &NameBindingKind::Import { binding: b2, .. }) => (b1, b2), + _ => continue, + }; + let (b1, b2) = match (orig_b1.vis, orig_b2.vis) { + (ty::Visibility::Public, ty::Visibility::Public) => continue, + (ty::Visibility::Public, _) => (b1, b2), + (_, ty::Visibility::Public) => (b2, b1), + _ => continue, + }; + resolution.binding = Some(self.arenas.alloc_name_binding(NameBinding { + kind: NameBindingKind::Ambiguity { b1: b1, b2: b2, legacy: true }, ..*b1 + })); } + _ => {} } } @@ -766,6 +790,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { } fn import_path_to_string(names: &[Ident], subclass: &ImportDirectiveSubclass) -> String { + let global = !names.is_empty() && names[0].name == keywords::CrateRoot.name(); + let names = if global { &names[1..] } else { names }; if names.is_empty() { import_directive_subclass_to_string(subclass) } else { diff --git a/src/librustc_save_analysis/data.rs b/src/librustc_save_analysis/data.rs index fc235aaf927..0a6281bf8c5 100644 --- a/src/librustc_save_analysis/data.rs +++ b/src/librustc_save_analysis/data.rs @@ -135,6 +135,7 @@ pub struct EnumData { pub variants: Vec<NodeId>, pub visibility: Visibility, pub docs: String, + pub sig: Signature, } /// Data for extern crates. @@ -169,6 +170,7 @@ pub struct FunctionData { pub visibility: Visibility, pub parent: Option<DefId>, pub docs: String, + pub sig: Signature, } /// Data about a function call. @@ -253,6 +255,7 @@ pub struct MethodData { pub parent: Option<DefId>, pub visibility: Visibility, pub docs: String, + pub sig: Signature, } /// Data for modules. @@ -267,6 +270,7 @@ pub struct ModData { pub items: Vec<NodeId>, pub visibility: Visibility, pub docs: String, + pub sig: Signature, } /// Data for a reference to a module. @@ -290,6 +294,7 @@ pub struct StructData { pub fields: Vec<NodeId>, pub visibility: Visibility, pub docs: String, + pub sig: Signature, } #[derive(Debug, RustcEncodable)] @@ -303,6 +308,7 @@ pub struct StructVariantData { pub scope: NodeId, pub parent: Option<DefId>, pub docs: String, + pub sig: Signature, } #[derive(Debug, RustcEncodable)] @@ -316,6 +322,7 @@ pub struct TraitData { pub items: Vec<NodeId>, pub visibility: Visibility, pub docs: String, + pub sig: Signature, } #[derive(Debug, RustcEncodable)] @@ -329,6 +336,7 @@ pub struct TupleVariantData { pub scope: NodeId, pub parent: Option<DefId>, pub docs: String, + pub sig: Signature, } /// Data for a typedef. @@ -342,6 +350,7 @@ pub struct TypeDefData { pub visibility: Visibility, pub parent: Option<DefId>, pub docs: String, + pub sig: Option<Signature>, } /// Data for a reference to a type or trait. @@ -386,6 +395,7 @@ pub struct VariableData { pub type_value: String, pub visibility: Visibility, pub docs: String, + pub sig: Option<Signature>, } #[derive(Debug, RustcEncodable)] @@ -405,3 +415,28 @@ pub struct VariableRefData { pub scope: NodeId, pub ref_id: DefId, } + + +/// Encodes information about the signature of a definition. This should have +/// enough information to create a nice display about a definition without +/// access to the source code. +#[derive(Clone, Debug, RustcEncodable)] +pub struct Signature { + pub span: Span, + pub text: String, + // These identify the main identifier for the defintion as byte offsets into + // `text`. E.g., of `foo` in `pub fn foo(...)` + pub ident_start: usize, + pub ident_end: usize, + pub defs: Vec<SigElement>, + pub refs: Vec<SigElement>, +} + +/// An element of a signature. `start` and `end` are byte offsets into the `text` +/// of the parent `Signature`. +#[derive(Clone, Debug, RustcEncodable)] +pub struct SigElement { + pub id: DefId, + pub start: usize, + pub end: usize, +} diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index afa78a05a63..093a739c69f 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -143,19 +143,20 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { // 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() < path.segments.len() { + 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(), - path.segments.len()); + segments.len()); for s in &spans { let loc = self.sess.codemap().lookup_char_pos(s.lo); error!(" '{}' in {}, line {}", @@ -170,14 +171,13 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { let mut result: Vec<(Span, String)> = vec![]; let mut segs = vec![]; - for (i, (seg, span)) in path.segments.iter().zip(&spans).enumerate() { + for (i, (seg, span)) in segments.iter().zip(&spans).enumerate() { segs.push(seg.clone()); let sub_path = ast::Path { span: *span, // span for the last segment - global: path.global, segments: segs, }; - let qualname = if i == 0 && path.global { + let qualname = if i == 0 && path.is_global() { format!("::{}", path_to_string(&sub_path)) } else { path_to_string(&sub_path) @@ -189,20 +189,11 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { result } - // The global arg allows us to override the global-ness of the path (which - // actually means 'does the path start with `::`', rather than 'is the path - // semantically global). We use the override for `use` imports (etc.) where - // the syntax is non-global, but the semantics are global. - fn write_sub_paths(&mut self, path: &ast::Path, global: bool) { + fn write_sub_paths(&mut self, path: &ast::Path) { let sub_paths = self.process_path_prefixes(path); - for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() { - let qualname = if i == 0 && global && !path.global { - format!("::{}", qualname) - } else { - qualname.clone() - }; + for (span, qualname) in sub_paths { self.dumper.mod_ref(ModRefData { - span: *span, + span: span, qualname: qualname, scope: self.cur_scope, ref_id: None @@ -212,22 +203,16 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { // As write_sub_paths, but does not process the last ident in the path (assuming it // will be processed elsewhere). See note on write_sub_paths about global. - fn write_sub_paths_truncated(&mut self, path: &ast::Path, global: bool) { + fn write_sub_paths_truncated(&mut self, path: &ast::Path) { let sub_paths = self.process_path_prefixes(path); let len = sub_paths.len(); if len <= 1 { return; } - let sub_paths = &sub_paths[..len-1]; - for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() { - let qualname = if i == 0 && global && !path.global { - format!("::{}", qualname) - } else { - qualname.clone() - }; + for (span, qualname) in sub_paths.into_iter().take(len - 1) { self.dumper.mod_ref(ModRefData { - span: *span, + span: span, qualname: qualname, scope: self.cur_scope, ref_id: None @@ -372,6 +357,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { parent: None, visibility: Visibility::Inherited, docs: String::new(), + sig: None, }.lower(self.tcx)); } } @@ -444,6 +430,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { parent: trait_id, visibility: vis, docs: docs_for_attrs(attrs), + sig: method_data.sig, }.lower(self.tcx)); } @@ -515,6 +502,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { visibility: Visibility::Inherited, parent: None, docs: String::new(), + sig: None, }.lower(self.tcx)); } } @@ -587,6 +575,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { parent: Some(parent_id), visibility: vis, docs: docs_for_attrs(attrs), + sig: None, }.lower(self.tcx)); } @@ -630,11 +619,10 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { fields: fields, visibility: From::from(&item.vis), docs: docs_for_attrs(&item.attrs), + sig: self.save_ctxt.sig_base(item), }.lower(self.tcx)); } - - // fields for field in def.fields() { self.process_struct_field_def(field, item.id); self.visit_ty(&field.ty); @@ -663,6 +651,18 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { qualname.push_str("::"); qualname.push_str(&name); + let text = self.span.signature_string_for_span(variant.span); + let ident_start = text.find(&name).unwrap(); + let ident_end = ident_start + name.len(); + let sig = Signature { + span: variant.span, + text: text, + ident_start: ident_start, + ident_end: ident_end, + defs: vec![], + refs: vec![], + }; + match variant.node.data { ast::VariantData::Struct(ref fields, _) => { let sub_span = self.span.span_for_first_ident(variant.span); @@ -684,6 +684,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { scope: enum_data.scope, parent: Some(make_def_id(item.id, &self.tcx.map)), docs: docs_for_attrs(&variant.node.attrs), + sig: sig, }.lower(self.tcx)); } } @@ -709,6 +710,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { scope: enum_data.scope, parent: Some(make_def_id(item.id, &self.tcx.map)), docs: docs_for_attrs(&variant.node.attrs), + sig: sig, }.lower(self.tcx)); } } @@ -793,6 +795,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { items: methods.iter().map(|i| i.id).collect(), visibility: From::from(&item.vis), docs: docs_for_attrs(&item.attrs), + sig: self.save_ctxt.sig_base(item), }.lower(self.tcx)); } @@ -935,7 +938,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { Def::Union(..) | Def::Variant(..) | Def::TyAlias(..) | - Def::AssociatedTy(..) => self.write_sub_paths_truncated(path, false), + Def::AssociatedTy(..) => self.write_sub_paths_truncated(path), _ => {} } } @@ -946,7 +949,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { fields: &'l [ast::Field], variant: &'l ty::VariantDef, base: &'l Option<P<ast::Expr>>) { - self.write_sub_paths_truncated(path, false); + self.write_sub_paths_truncated(path); if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) { down_cast_data!(struct_lit_data, TypeRefData, ex.span); @@ -1058,6 +1061,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { parent: None, visibility: Visibility::Inherited, docs: String::new(), + sig: None, }.lower(self.tcx)); } } @@ -1201,7 +1205,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, visibility: From::from(&item.vis), }.lower(self.tcx)); } - self.write_sub_paths_truncated(path, true); + self.write_sub_paths_truncated(path); } ast::ViewPathGlob(ref path) => { // Make a comma-separated list of names of imported modules. @@ -1225,7 +1229,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, visibility: From::from(&item.vis), }.lower(self.tcx)); } - self.write_sub_paths(path, true); + self.write_sub_paths(path); } ast::ViewPathList(ref path, ref list) => { for plid in list { @@ -1237,7 +1241,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, } } - self.write_sub_paths(path, true); + self.write_sub_paths(path); } } } @@ -1272,10 +1276,10 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, Struct(ref def, ref ty_params) => self.process_struct(item, def, ty_params), Enum(ref def, ref ty_params) => self.process_enum(item, def, ty_params), Impl(.., - ref ty_params, - ref trait_ref, - ref typ, - ref impl_items) => { + ref ty_params, + ref trait_ref, + ref typ, + ref impl_items) => { self.process_impl(item, ty_params, trait_ref, &typ, impl_items) } Trait(_, ref generics, ref trait_refs, ref methods) => @@ -1298,6 +1302,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, visibility: From::from(&item.vis), parent: None, docs: docs_for_attrs(&item.attrs), + sig: Some(self.save_ctxt.sig_base(item)), }.lower(self.tcx)); } @@ -1340,7 +1345,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, }.lower(self.tcx)); } - self.write_sub_paths_truncated(path, false); + self.write_sub_paths_truncated(path); visit::walk_path(self, path); } @@ -1507,6 +1512,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, parent: None, visibility: Visibility::Inherited, docs: String::new(), + sig: None, }.lower(self.tcx)); } } diff --git a/src/librustc_save_analysis/external_data.rs b/src/librustc_save_analysis/external_data.rs index 58475757423..18ae3a7fa9e 100644 --- a/src/librustc_save_analysis/external_data.rs +++ b/src/librustc_save_analysis/external_data.rs @@ -15,7 +15,7 @@ use syntax::ast::NodeId; use syntax::codemap::CodeMap; use syntax_pos::Span; -use data::{self, Visibility}; +use data::{self, Visibility, SigElement}; // FIXME: this should be pub(crate), but the current snapshot doesn't allow it yet pub trait Lower { @@ -97,6 +97,7 @@ pub struct EnumData { pub variants: Vec<DefId>, pub visibility: Visibility, pub docs: String, + pub sig: Signature, } impl Lower for data::EnumData { @@ -113,6 +114,7 @@ impl Lower for data::EnumData { variants: self.variants.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(), visibility: self.visibility, docs: self.docs, + sig: self.sig.lower(tcx), } } } @@ -176,6 +178,7 @@ pub struct FunctionData { pub visibility: Visibility, pub parent: Option<DefId>, pub docs: String, + pub sig: Signature, } impl Lower for data::FunctionData { @@ -193,6 +196,7 @@ impl Lower for data::FunctionData { visibility: self.visibility, parent: self.parent, docs: self.docs, + sig: self.sig.lower(tcx), } } } @@ -341,6 +345,7 @@ pub struct MethodData { pub visibility: Visibility, pub parent: Option<DefId>, pub docs: String, + pub sig: Signature, } impl Lower for data::MethodData { @@ -358,6 +363,7 @@ impl Lower for data::MethodData { visibility: self.visibility, parent: self.parent, docs: self.docs, + sig: self.sig.lower(tcx), } } } @@ -374,6 +380,7 @@ pub struct ModData { pub items: Vec<DefId>, pub visibility: Visibility, pub docs: String, + pub sig: Signature, } impl Lower for data::ModData { @@ -390,6 +397,7 @@ impl Lower for data::ModData { items: self.items.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(), visibility: self.visibility, docs: self.docs, + sig: self.sig.lower(tcx), } } } @@ -428,6 +436,7 @@ pub struct StructData { pub fields: Vec<DefId>, pub visibility: Visibility, pub docs: String, + pub sig: Signature, } impl Lower for data::StructData { @@ -445,6 +454,7 @@ impl Lower for data::StructData { fields: self.fields.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(), visibility: self.visibility, docs: self.docs, + sig: self.sig.lower(tcx), } } } @@ -460,6 +470,7 @@ pub struct StructVariantData { pub scope: DefId, pub parent: Option<DefId>, pub docs: String, + pub sig: Signature, } impl Lower for data::StructVariantData { @@ -476,6 +487,7 @@ impl Lower for data::StructVariantData { scope: make_def_id(self.scope, &tcx.map), parent: self.parent, docs: self.docs, + sig: self.sig.lower(tcx), } } } @@ -491,6 +503,7 @@ pub struct TraitData { pub items: Vec<DefId>, pub visibility: Visibility, pub docs: String, + pub sig: Signature, } impl Lower for data::TraitData { @@ -507,6 +520,7 @@ impl Lower for data::TraitData { items: self.items.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(), visibility: self.visibility, docs: self.docs, + sig: self.sig.lower(tcx), } } } @@ -522,6 +536,7 @@ pub struct TupleVariantData { pub scope: DefId, pub parent: Option<DefId>, pub docs: String, + pub sig: Signature, } impl Lower for data::TupleVariantData { @@ -538,6 +553,7 @@ impl Lower for data::TupleVariantData { scope: make_def_id(self.scope, &tcx.map), parent: self.parent, docs: self.docs, + sig: self.sig.lower(tcx), } } } @@ -553,6 +569,7 @@ pub struct TypeDefData { pub visibility: Visibility, pub parent: Option<DefId>, pub docs: String, + pub sig: Option<Signature>, } impl Lower for data::TypeDefData { @@ -568,6 +585,7 @@ impl Lower for data::TypeDefData { visibility: self.visibility, parent: self.parent, docs: self.docs, + sig: self.sig.map(|s| s.lower(tcx)), } } } @@ -656,6 +674,7 @@ pub struct VariableData { pub parent: Option<DefId>, pub visibility: Visibility, pub docs: String, + pub sig: Option<Signature>, } impl Lower for data::VariableData { @@ -674,6 +693,7 @@ impl Lower for data::VariableData { parent: self.parent, visibility: self.visibility, docs: self.docs, + sig: self.sig.map(|s| s.lower(tcx)), } } } @@ -700,3 +720,30 @@ impl Lower for data::VariableRefData { } } } + +#[derive(Clone, Debug, RustcEncodable)] +pub struct Signature { + pub span: SpanData, + pub text: String, + // These identify the main identifier for the defintion as byte offsets into + // `text`. E.g., of `foo` in `pub fn foo(...)` + pub ident_start: usize, + pub ident_end: usize, + pub defs: Vec<SigElement>, + pub refs: Vec<SigElement>, +} + +impl Lower for data::Signature { + type Target = Signature; + + fn lower(self, tcx: TyCtxt) -> Signature { + Signature { + span: SpanData::from_span(self.span, tcx.sess.codemap()), + text: self.text, + ident_start: self.ident_start, + ident_end: self.ident_end, + defs: self.defs, + refs: self.refs, + } + } +} diff --git a/src/librustc_save_analysis/json_api_dumper.rs b/src/librustc_save_analysis/json_api_dumper.rs index d56aae18a7c..342c33af2f8 100644 --- a/src/librustc_save_analysis/json_api_dumper.rs +++ b/src/librustc_save_analysis/json_api_dumper.rs @@ -14,7 +14,7 @@ use rustc::hir::def_id::DefId; use rustc_serialize::json::as_json; use external_data::*; -use data::{VariableKind, Visibility}; +use data::{VariableKind, Visibility, SigElement}; use dump::Dump; use super::Format; @@ -179,6 +179,7 @@ struct Def { children: Vec<Id>, decl_id: Option<Id>, docs: String, + sig: Option<JsonSignature>, } #[derive(Debug, RustcEncodable)] @@ -221,6 +222,7 @@ impl From<EnumData> for Option<Def> { children: data.variants.into_iter().map(|id| From::from(id)).collect(), decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), }), _ => None, } @@ -240,6 +242,7 @@ impl From<TupleVariantData> for Option<Def> { children: vec![], decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), }) } } @@ -256,6 +259,7 @@ impl From<StructVariantData> for Option<Def> { children: vec![], decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), }) } } @@ -273,6 +277,7 @@ impl From<StructData> for Option<Def> { children: data.fields.into_iter().map(|id| From::from(id)).collect(), decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), }), _ => None, } @@ -292,6 +297,7 @@ impl From<TraitData> for Option<Def> { parent: None, decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), }), _ => None, } @@ -311,6 +317,7 @@ impl From<FunctionData> for Option<Def> { parent: data.parent.map(|id| From::from(id)), decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), }), _ => None, } @@ -330,6 +337,7 @@ impl From<MethodData> for Option<Def> { parent: data.parent.map(|id| From::from(id)), decl_id: data.decl_id.map(|id| From::from(id)), docs: data.docs, + sig: Some(From::from(data.sig)), }), _ => None, } @@ -348,6 +356,7 @@ impl From<MacroData> for Option<Def> { parent: None, decl_id: None, docs: data.docs, + sig: None, }) } } @@ -365,6 +374,7 @@ impl From<ModData> for Option<Def> { parent: None, decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), }), _ => None, } @@ -384,11 +394,13 @@ impl From<TypeDefData> for Option<Def> { parent: data.parent.map(|id| From::from(id)), decl_id: None, docs: String::new(), + sig: data.sig.map(|s| From::from(s)), }), _ => None, } } } + impl From<VariableData> for Option<Def> { fn from(data: VariableData) -> Option<Def> { match data.visibility { @@ -408,8 +420,49 @@ impl From<VariableData> for Option<Def> { parent: data.parent.map(|id| From::from(id)), decl_id: None, docs: data.docs, + sig: data.sig.map(|s| From::from(s)), }), _ => None, } } } + +#[derive(Debug, RustcEncodable)] +pub struct JsonSignature { + span: SpanData, + text: String, + ident_start: usize, + ident_end: usize, + defs: Vec<JsonSigElement>, + refs: Vec<JsonSigElement>, +} + +impl From<Signature> for JsonSignature { + fn from(data: Signature) -> JsonSignature { + JsonSignature { + span: data.span, + text: data.text, + ident_start: data.ident_start, + ident_end: data.ident_end, + defs: data.defs.into_iter().map(|s| From::from(s)).collect(), + refs: data.refs.into_iter().map(|s| From::from(s)).collect(), + } + } +} + +#[derive(Debug, RustcEncodable)] +pub struct JsonSigElement { + id: Id, + start: usize, + end: usize, +} + +impl From<SigElement> for JsonSigElement { + fn from(data: SigElement) -> JsonSigElement { + JsonSigElement { + id: From::from(data.id), + start: data.start, + end: data.end, + } + } +} diff --git a/src/librustc_save_analysis/json_dumper.rs b/src/librustc_save_analysis/json_dumper.rs index f97272ad544..16c06a556df 100644 --- a/src/librustc_save_analysis/json_dumper.rs +++ b/src/librustc_save_analysis/json_dumper.rs @@ -14,7 +14,7 @@ use rustc::hir::def_id::DefId; use rustc_serialize::json::as_json; use external_data::*; -use data::VariableKind; +use data::{VariableKind, SigElement}; use dump::Dump; use super::Format; @@ -86,6 +86,7 @@ impl<'b, W: Write + 'b> Dump for JsonDumper<'b, W> { children: data.items.into_iter().map(|id| From::from(id)).collect(), decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), }; if def.span.file_name != def.value { // If the module is an out-of-line defintion, then we'll make the @@ -223,6 +224,7 @@ struct Def { children: Vec<Id>, decl_id: Option<Id>, docs: String, + sig: Option<JsonSignature>, } #[derive(Debug, RustcEncodable)] @@ -264,6 +266,7 @@ impl From<EnumData> for Def { children: data.variants.into_iter().map(|id| From::from(id)).collect(), decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), } } } @@ -280,6 +283,7 @@ impl From<TupleVariantData> for Def { children: vec![], decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), } } } @@ -295,6 +299,7 @@ impl From<StructVariantData> for Def { children: vec![], decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), } } } @@ -310,6 +315,7 @@ impl From<StructData> for Def { children: data.fields.into_iter().map(|id| From::from(id)).collect(), decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), } } } @@ -325,6 +331,7 @@ impl From<TraitData> for Def { children: data.items.into_iter().map(|id| From::from(id)).collect(), decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), } } } @@ -340,6 +347,7 @@ impl From<FunctionData> for Def { children: vec![], decl_id: None, docs: data.docs, + sig: Some(From::from(data.sig)), } } } @@ -355,6 +363,7 @@ impl From<MethodData> for Def { children: vec![], decl_id: data.decl_id.map(|id| From::from(id)), docs: data.docs, + sig: Some(From::from(data.sig)), } } } @@ -370,10 +379,10 @@ impl From<MacroData> for Def { children: vec![], decl_id: None, docs: data.docs, + sig: None, } } } - impl From<TypeDefData> for Def { fn from(data: TypeDefData) -> Def { Def { @@ -386,6 +395,7 @@ impl From<TypeDefData> for Def { children: vec![], decl_id: None, docs: String::new(), + sig: data.sig.map(|s| From::from(s)), } } } @@ -406,6 +416,7 @@ impl From<VariableData> for Def { children: vec![], decl_id: None, docs: data.docs, + sig: None, } } } @@ -496,3 +507,43 @@ impl From<MacroUseData> for MacroRef { } } } + +#[derive(Debug, RustcEncodable)] +pub struct JsonSignature { + span: SpanData, + text: String, + ident_start: usize, + ident_end: usize, + defs: Vec<JsonSigElement>, + refs: Vec<JsonSigElement>, +} + +impl From<Signature> for JsonSignature { + fn from(data: Signature) -> JsonSignature { + JsonSignature { + span: data.span, + text: data.text, + ident_start: data.ident_start, + ident_end: data.ident_end, + defs: data.defs.into_iter().map(|s| From::from(s)).collect(), + refs: data.refs.into_iter().map(|s| From::from(s)).collect(), + } + } +} + +#[derive(Debug, RustcEncodable)] +pub struct JsonSigElement { + id: Id, + start: usize, + end: usize, +} + +impl From<SigElement> for JsonSigElement { + fn from(data: SigElement) -> JsonSigElement { + JsonSigElement { + id: From::from(data.id), + start: data.start, + end: data.end, + } + } +} diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 0c910240b60..491521a3239 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -48,7 +48,7 @@ use rustc::session::config::CrateType::CrateTypeExecutable; use rustc::ty::{self, TyCtxt}; use std::env; -use std::fs::{self, File}; +use std::fs::File; use std::path::{Path, PathBuf}; use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID}; @@ -152,6 +152,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { visibility: From::from(&item.vis), parent: None, docs: docs_for_attrs(&item.attrs), + sig: self.sig_base(item), })) } ast::ItemKind::Static(ref typ, mt, ref expr) => { @@ -179,6 +180,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { type_value: ty_to_string(&typ), visibility: From::from(&item.vis), docs: docs_for_attrs(&item.attrs), + sig: Some(self.sig_base(item)), })) } ast::ItemKind::Const(ref typ, ref expr) => { @@ -197,6 +199,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { type_value: ty_to_string(&typ), visibility: From::from(&item.vis), docs: docs_for_attrs(&item.attrs), + sig: Some(self.sig_base(item)), })) } ast::ItemKind::Mod(ref m) => { @@ -207,6 +210,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod); filter!(self.span_utils, sub_span, item.span, None); + Some(Data::ModData(ModData { id: item.id, name: item.ident.to_string(), @@ -217,6 +221,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { items: m.items.iter().map(|i| i.id).collect(), visibility: From::from(&item.vis), docs: docs_for_attrs(&item.attrs), + sig: self.sig_base(item), })) } ast::ItemKind::Enum(ref def, _) => { @@ -239,6 +244,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { variants: def.variants.iter().map(|v| v.node.data.id()).collect(), visibility: From::from(&item.vis), docs: docs_for_attrs(&item.attrs), + sig: self.sig_base(item), })) } ast::ItemKind::Impl(.., ref trait_ref, ref typ, _) => { @@ -287,18 +293,34 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } - pub fn get_field_data(&self, field: &ast::StructField, - scope: NodeId) -> Option<VariableData> { + pub fn get_field_data(&self, + field: &ast::StructField, + scope: NodeId) + -> Option<VariableData> { if let Some(ident) = field.ident { + let name = ident.to_string(); let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident); - let def_id = self.tcx.map.local_def_id(field.id); - let typ = self.tcx.item_type(def_id).to_string(); let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon); filter!(self.span_utils, sub_span, field.span, None); + let def_id = self.tcx.map.local_def_id(field.id); + let typ = self.tcx.item_type(def_id).to_string(); + + let span = field.span; + let text = self.span_utils.snippet(field.span); + let ident_start = text.find(&name).unwrap(); + let ident_end = ident_start + name.len(); + let sig = Signature { + span: span, + text: text, + ident_start: ident_start, + ident_end: ident_end, + defs: vec![], + refs: vec![], + }; Some(VariableData { id: field.id, kind: VariableKind::Field, - name: ident.to_string(), + name: name, qualname: qualname, span: sub_span.unwrap(), scope: scope, @@ -307,6 +329,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { type_value: typ, visibility: From::from(&field.vis), docs: docs_for_attrs(&field.attrs), + sig: Some(sig), }) } else { None @@ -388,9 +411,23 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn); filter!(self.span_utils, sub_span, span, None); + + let name = name.to_string(); + let text = self.span_utils.signature_string_for_span(span); + let ident_start = text.find(&name).unwrap(); + let ident_end = ident_start + name.len(); + let sig = Signature { + span: span, + text: text, + ident_start: ident_start, + ident_end: ident_end, + defs: vec![], + refs: vec![], + }; + Some(FunctionData { id: id, - name: name.to_string(), + name: name, qualname: qualname, declaration: decl_id, span: sub_span.unwrap(), @@ -400,6 +437,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { visibility: vis, parent: parent_scope, docs: docs, + sig: sig, }) } @@ -695,6 +733,21 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } + fn sig_base(&self, item: &ast::Item) -> Signature { + let text = self.span_utils.signature_string_for_span(item.span); + let name = item.ident.to_string(); + let ident_start = text.find(&name).expect("Name not in signature?"); + let ident_end = ident_start + name.len(); + Signature { + span: mk_sp(item.span.lo, item.span.lo + BytePos(text.len() as u32)), + text: text, + ident_start: ident_start, + ident_end: ident_end, + defs: vec![], + refs: vec![], + } + } + #[inline] pub fn enclosing_scope(&self, id: NodeId) -> NodeId { self.tcx.map.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID) @@ -832,7 +885,7 @@ pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>, }, }; - if let Err(e) = fs::create_dir_all(&root_path) { + if let Err(e) = rustc::util::fs::create_dir_racy(&root_path) { tcx.sess.err(&format!("Could not create directory {}: {}", root_path.display(), e)); diff --git a/src/librustc_save_analysis/span_utils.rs b/src/librustc_save_analysis/span_utils.rs index e06aefd865f..448bb2e7617 100644 --- a/src/librustc_save_analysis/span_utils.rs +++ b/src/librustc_save_analysis/span_utils.rs @@ -19,7 +19,9 @@ use std::path::Path; use syntax::ast; use syntax::parse::lexer::{self, Reader, StringReader}; use syntax::parse::token::{self, Token}; +use syntax::parse::parser::Parser; use syntax::symbol::keywords; +use syntax::tokenstream::TokenTree; use syntax_pos::*; #[derive(Clone)] @@ -87,6 +89,12 @@ impl<'a> SpanUtils<'a> { lexer::StringReader::new(s.diagnostic(), filemap) } + fn span_to_tts(&self, span: Span) -> Vec<TokenTree> { + let srdr = self.retokenise_span(span); + let mut p = Parser::new(&self.sess.parse_sess, Box::new(srdr), None, false); + p.parse_all_token_trees().expect("Couldn't re-parse span") + } + // Re-parses a path and returns the span for the last identifier in the path pub fn span_for_last_ident(&self, span: Span) -> Option<Span> { let mut result = None; @@ -308,6 +316,42 @@ impl<'a> SpanUtils<'a> { } } + /// `span` must be the span for an item such as a function or struct. This + /// function returns the program text from the start of the span until the + /// end of the 'signature' part, that is up to, but not including an opening + /// brace or semicolon. + pub fn signature_string_for_span(&self, span: Span) -> String { + let mut toks = self.span_to_tts(span).into_iter(); + let mut prev = toks.next().unwrap(); + let first_span = prev.get_span(); + let mut angle_count = 0; + for tok in toks { + if let TokenTree::Token(_, ref tok) = prev { + angle_count += match *tok { + token::Eof => { break; } + token::Lt => 1, + token::Gt => -1, + token::BinOp(token::Shl) => 2, + token::BinOp(token::Shr) => -2, + _ => 0, + }; + } + if angle_count > 0 { + prev = tok; + continue; + } + if let TokenTree::Token(_, token::Semi) = tok { + return self.snippet(mk_sp(first_span.lo, prev.get_span().hi)); + } else if let TokenTree::Delimited(_, ref d) = tok { + if d.delim == token::Brace { + return self.snippet(mk_sp(first_span.lo, prev.get_span().hi)); + } + } + prev = tok; + } + self.snippet(span) + } + pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option<Span> { let mut toks = self.retokenise_span(span); let mut prev = toks.real_token(); diff --git a/src/librustc_trans/README.md b/src/librustc_trans/README.md new file mode 100644 index 00000000000..cd43cbd7052 --- /dev/null +++ b/src/librustc_trans/README.md @@ -0,0 +1 @@ +See [librustc/README.md](../librustc/README.md). diff --git a/src/librustc_trans/README.txt b/src/librustc_trans/README.txt deleted file mode 100644 index 1f416d5404d..00000000000 --- a/src/librustc_trans/README.txt +++ /dev/null @@ -1 +0,0 @@ -See the README.md in ../librustc. diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 0ac853e99ee..9c4246e079b 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -10,7 +10,6 @@ use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace}; use base; -use build::AllocaFcx; use common::{type_is_fat_ptr, BlockAndBuilder, C_uint}; use context::CrateContext; use cabi_x86; @@ -25,6 +24,7 @@ use cabi_mips; use cabi_mips64; use cabi_asmjs; use cabi_msp430; +use cabi_sparc; use machine::{llalign_of_min, llsize_of, llsize_of_alloc}; use type_::Type; use type_of; @@ -59,7 +59,7 @@ mod attr_impl { // The subset of llvm::Attribute needed for arguments, packed into a bitfield. bitflags! { #[derive(Default, Debug)] - flags ArgAttribute : u8 { + flags ArgAttribute : u16 { const ByVal = 1 << 0, const NoAlias = 1 << 1, const NoCapture = 1 << 2, @@ -68,6 +68,7 @@ mod attr_impl { const SExt = 1 << 5, const StructRet = 1 << 6, const ZExt = 1 << 7, + const InReg = 1 << 8, } } } @@ -81,7 +82,7 @@ macro_rules! for_each_kind { impl ArgAttribute { fn for_each_kind<F>(&self, mut f: F) where F: FnMut(llvm::Attribute) { for_each_kind!(self, f, - ByVal, NoAlias, NoCapture, NonNull, ReadOnly, SExt, StructRet, ZExt) + ByVal, NoAlias, NoCapture, NonNull, ReadOnly, SExt, StructRet, ZExt, InReg) } } @@ -99,21 +100,11 @@ impl ArgAttributes { self } - pub fn unset(&mut self, attr: ArgAttribute) -> &mut Self { - self.regular = self.regular - attr; - self - } - pub fn set_dereferenceable(&mut self, bytes: u64) -> &mut Self { self.dereferenceable_bytes = bytes; self } - pub fn unset_dereferenceable(&mut self) -> &mut Self { - self.dereferenceable_bytes = 0; - self - } - pub fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef) { unsafe { self.regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn)); @@ -246,7 +237,7 @@ impl ArgType { if self.is_ignore() { return; } - let ccx = bcx.ccx(); + let ccx = bcx.ccx; if self.is_indirect() { let llsz = llsize_of(ccx, self.ty); let llalign = llalign_of_min(ccx, self.ty); @@ -278,7 +269,7 @@ impl ArgType { // bitcasting to the struct type yields invalid cast errors. // We instead thus allocate some scratch space... - let llscratch = AllocaFcx(bcx.fcx(), ty, "abi_cast"); + let llscratch = bcx.fcx().alloca(ty, "abi_cast"); base::Lifetime::Start.call(bcx, llscratch); // ...where we first store the value... @@ -431,7 +422,7 @@ impl FnType { let ret_ty = sig.output(); let mut ret = arg_of(ret_ty, true); - if !type_is_fat_ptr(ccx.tcx(), ret_ty) { + if !type_is_fat_ptr(ccx, ret_ty) { // The `noalias` attribute on the return value is useful to a // function ptr caller. if let ty::TyBox(_) = ret_ty.sty { @@ -496,7 +487,7 @@ impl FnType { for ty in inputs.iter().chain(extra_args.iter()) { let mut arg = arg_of(ty, false); - if type_is_fat_ptr(ccx.tcx(), ty) { + if type_is_fat_ptr(ccx, ty) { let original_tys = arg.original_ty.field_types(); let sizing_tys = arg.ty.field_types(); assert_eq!((original_tys.len(), sizing_tys.len()), (2, 2)); @@ -569,7 +560,7 @@ impl FnType { }; // Fat pointers are returned by-value. if !self.ret.is_ignore() { - if !type_is_fat_ptr(ccx.tcx(), sig.output()) { + if !type_is_fat_ptr(ccx, sig.output()) { fixup(&mut self.ret); } } @@ -584,7 +575,14 @@ impl FnType { } match &ccx.sess().target.target.arch[..] { - "x86" => cabi_x86::compute_abi_info(ccx, self), + "x86" => { + let flavor = if abi == Abi::Fastcall { + cabi_x86::Flavor::Fastcall + } else { + cabi_x86::Flavor::General + }; + cabi_x86::compute_abi_info(ccx, self, flavor); + }, "x86_64" => if abi == Abi::SysV64 { cabi_x86_64::compute_abi_info(ccx, self); } else if abi == Abi::Win64 || ccx.sess().target.target.options.is_like_windows { @@ -609,6 +607,7 @@ impl FnType { "asmjs" => cabi_asmjs::compute_abi_info(ccx, self), "wasm32" => cabi_asmjs::compute_abi_info(ccx, self), "msp430" => cabi_msp430::compute_abi_info(ccx, self), + "sparc" => cabi_sparc::compute_abi_info(ccx, self), a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a)) } diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 8ee362bae35..31a5538a3c1 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -48,9 +48,7 @@ use std; use llvm::{ValueRef, True, IntEQ, IntNE}; use rustc::ty::layout; use rustc::ty::{self, Ty, AdtKind}; -use build::*; use common::*; -use debuginfo::DebugLoc; use glue; use base; use machine; @@ -151,14 +149,14 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, | layout::UntaggedUnion { .. } | layout::RawNullablePointer { .. } => { } layout::Univariant { ..} | layout::StructWrappedNullablePointer { .. } => { - let (nonnull_variant, packed) = match *l { - layout::Univariant { ref variant, .. } => (0, variant.packed), + let (nonnull_variant_index, nonnull_variant, packed) = match *l { + layout::Univariant { ref variant, .. } => (0, variant, variant.packed), layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => - (nndiscr, nonnull.packed), + (nndiscr, nonnull, nonnull.packed), _ => unreachable!() }; - let fields = compute_fields(cx, t, nonnull_variant as usize, true); - llty.set_struct_body(&struct_llfields(cx, &fields, false, false), + let fields = compute_fields(cx, t, nonnull_variant_index as usize, true); + llty.set_struct_body(&struct_llfields(cx, &fields, nonnull_variant, false, false), packed) }, _ => bug!("This function cannot handle {} with layout {:#?}", t, l) @@ -188,7 +186,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let fields = compute_fields(cx, t, nndiscr as usize, false); match name { None => { - Type::struct_(cx, &struct_llfields(cx, &fields, sizing, dst), + Type::struct_(cx, &struct_llfields(cx, &fields, nonnull, sizing, dst), nonnull.packed) } Some(name) => { @@ -203,7 +201,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let fields = compute_fields(cx, t, 0, true); match name { None => { - let fields = struct_llfields(cx, &fields, sizing, dst); + let fields = struct_llfields(cx, &fields, &variant, sizing, dst); Type::struct_(cx, &fields, variant.packed) } Some(name) => { @@ -291,23 +289,26 @@ fn union_fill(cx: &CrateContext, size: u64, align: u64) -> Type { fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fields: &Vec<Ty<'tcx>>, + variant: &layout::Struct, sizing: bool, dst: bool) -> Vec<Type> { + let fields = variant.field_index_by_increasing_offset().map(|i| fields[i as usize]); if sizing { - fields.iter().filter(|&ty| !dst || type_is_sized(cx.tcx(), *ty)) - .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() + fields.filter(|ty| !dst || cx.shared().type_is_sized(*ty)) + .map(|ty| type_of::sizing_type_of(cx, ty)).collect() } else { - fields.iter().map(|&ty| type_of::in_memory_type_of(cx, ty)).collect() + fields.map(|ty| type_of::in_memory_type_of(cx, ty)).collect() } } /// Obtain a representation of the discriminant sufficient to translate /// destructuring; this may or may not involve the actual discriminant. -pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - t: Ty<'tcx>, - scrutinee: ValueRef, - range_assert: bool) - -> (BranchKind, Option<ValueRef>) { - let l = bcx.ccx().layout_of(t); +pub fn trans_switch<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + t: Ty<'tcx>, + scrutinee: ValueRef, + range_assert: bool +) -> (BranchKind, Option<ValueRef>) { + let l = bcx.ccx.layout_of(t); match *l { layout::CEnum { .. } | layout::General { .. } | layout::RawNullablePointer { .. } | layout::StructWrappedNullablePointer { .. } => { @@ -329,34 +330,37 @@ pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool { } /// Obtain the actual discriminant of a value. -pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, - scrutinee: ValueRef, cast_to: Option<Type>, - range_assert: bool) - -> ValueRef { +pub fn trans_get_discr<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + t: Ty<'tcx>, + scrutinee: ValueRef, + cast_to: Option<Type>, + range_assert: bool +) -> ValueRef { let (def, substs) = match t.sty { ty::TyAdt(ref def, substs) if def.adt_kind() == AdtKind::Enum => (def, substs), _ => bug!("{} is not an enum", t) }; debug!("trans_get_discr t: {:?}", t); - let l = bcx.ccx().layout_of(t); + let l = bcx.ccx.layout_of(t); let val = match *l { layout::CEnum { discr, min, max, .. } => { load_discr(bcx, discr, scrutinee, min, max, range_assert) } layout::General { discr, .. } => { - let ptr = StructGEP(bcx, scrutinee, 0); + let ptr = bcx.struct_gep(scrutinee, 0); load_discr(bcx, discr, ptr, 0, def.variants.len() as u64 - 1, range_assert) } - layout::Univariant { .. } | layout::UntaggedUnion { .. } => C_u8(bcx.ccx(), 0), + layout::Univariant { .. } | layout::UntaggedUnion { .. } => C_u8(bcx.ccx, 0), layout::RawNullablePointer { nndiscr, .. } => { let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; - let llptrty = type_of::sizing_type_of(bcx.ccx(), - monomorphize::field_ty(bcx.ccx().tcx(), substs, + let llptrty = type_of::sizing_type_of(bcx.ccx, + monomorphize::field_ty(bcx.ccx.tcx(), substs, &def.variants[nndiscr as usize].fields[0])); - ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty), DebugLoc::None) + bcx.icmp(cmp, bcx.load(scrutinee), C_null(llptrty)) } layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee) @@ -365,24 +369,28 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, }; match cast_to { None => val, - Some(llty) => if is_discr_signed(&l) { SExt(bcx, val, llty) } else { ZExt(bcx, val, llty) } + Some(llty) => if is_discr_signed(&l) { bcx.sext(val, llty) } else { bcx.zext(val, llty) } } } -fn struct_wrapped_nullable_bitdiscr(bcx: Block, nndiscr: u64, discrfield: &layout::FieldPath, - scrutinee: ValueRef) -> ValueRef { - let llptrptr = GEPi(bcx, scrutinee, +fn struct_wrapped_nullable_bitdiscr( + bcx: &BlockAndBuilder, + nndiscr: u64, + discrfield: &layout::FieldPath, + scrutinee: ValueRef +) -> ValueRef { + let llptrptr = bcx.gepi(scrutinee, &discrfield.iter().map(|f| *f as usize).collect::<Vec<_>>()[..]); - let llptr = Load(bcx, llptrptr); + let llptr = bcx.load(llptrptr); let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; - ICmp(bcx, cmp, llptr, C_null(val_ty(llptr)), DebugLoc::None) + bcx.icmp(cmp, llptr, C_null(val_ty(llptr))) } /// Helper for cases where the discriminant is simply loaded. -fn load_discr(bcx: Block, ity: layout::Integer, ptr: ValueRef, min: u64, max: u64, +fn load_discr(bcx: &BlockAndBuilder, ity: layout::Integer, ptr: ValueRef, min: u64, max: u64, range_assert: bool) -> ValueRef { - let llty = Type::from_integer(bcx.ccx(), ity); + let llty = Type::from_integer(bcx.ccx, ity); assert_eq!(val_ty(ptr), llty.ptr_to()); let bits = ity.size().bits(); assert!(bits <= 64); @@ -395,11 +403,11 @@ fn load_discr(bcx: Block, ity: layout::Integer, ptr: ValueRef, min: u64, max: u6 // rejected by the LLVM verifier (it would mean either an // empty set, which is impossible, or the entire range of the // type, which is pointless). - Load(bcx, ptr) + bcx.load(ptr) } else { // llvm::ConstantRange can deal with ranges that wrap around, // so an overflow on (max + 1) is fine. - LoadRangeAssert(bcx, ptr, min, max.wrapping_add(1), /* signed: */ True) + bcx.load_range_assert(ptr, min, max.wrapping_add(1), /* signed: */ True) } } @@ -407,18 +415,17 @@ fn load_discr(bcx: Block, ity: layout::Integer, ptr: ValueRef, min: u64, max: u6 /// discriminant-like value returned by `trans_switch`. /// /// This should ideally be less tightly tied to `_match`. -pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, value: Disr) - -> ValueRef { - let l = bcx.ccx().layout_of(t); +pub fn trans_case<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, t: Ty<'tcx>, value: Disr) -> ValueRef { + let l = bcx.ccx.layout_of(t); match *l { layout::CEnum { discr, .. } | layout::General { discr, .. }=> { - C_integral(Type::from_integer(bcx.ccx(), discr), value.0, true) + C_integral(Type::from_integer(bcx.ccx, discr), value.0, true) } layout::RawNullablePointer { .. } | layout::StructWrappedNullablePointer { .. } => { assert!(value == Disr(0) || value == Disr(1)); - C_bool(bcx.ccx(), value != Disr(0)) + C_bool(bcx.ccx, value != Disr(0)) } _ => { bug!("{} does not have a discriminant. Represented as {:#?}", t, l); @@ -428,18 +435,19 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, value: Disr) /// Set the discriminant for a new value of the given case of the given /// representation. -pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, - val: ValueRef, to: Disr) { - let l = bcx.ccx().layout_of(t); +pub fn trans_set_discr<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr +) { + let l = bcx.ccx.layout_of(t); match *l { layout::CEnum{ discr, min, max, .. } => { assert_discr_in_range(Disr(min), Disr(max), to); - Store(bcx, C_integral(Type::from_integer(bcx.ccx(), discr), to.0, true), + bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true), val); } layout::General{ discr, .. } => { - Store(bcx, C_integral(Type::from_integer(bcx.ccx(), discr), to.0, true), - StructGEP(bcx, val, 0)); + bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true), + bcx.struct_gep(val, 0)); } layout::Univariant { .. } | layout::UntaggedUnion { .. } @@ -447,10 +455,10 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, assert_eq!(to, Disr(0)); } layout::RawNullablePointer { nndiscr, .. } => { - let nnty = compute_fields(bcx.ccx(), t, nndiscr as usize, false)[0]; + let nnty = compute_fields(bcx.ccx, t, nndiscr as usize, false)[0]; if to.0 != nndiscr { - let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty); - Store(bcx, C_null(llptrty), val); + let llptrty = type_of::sizing_type_of(bcx.ccx, nnty); + bcx.store(C_null(llptrty), val); } } layout::StructWrappedNullablePointer { nndiscr, ref discrfield, ref nonnull, .. } => { @@ -459,17 +467,16 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, // Issue #34427: As workaround for LLVM bug on // ARM, use memset of 0 on whole struct rather // than storing null to single target field. - let b = B(bcx); - let llptr = b.pointercast(val, Type::i8(b.ccx).ptr_to()); - let fill_byte = C_u8(b.ccx, 0); - let size = C_uint(b.ccx, nonnull.stride().bytes()); - let align = C_i32(b.ccx, nonnull.align.abi() as i32); - base::call_memset(&b, llptr, fill_byte, size, align, false); + let llptr = bcx.pointercast(val, Type::i8(bcx.ccx).ptr_to()); + let fill_byte = C_u8(bcx.ccx, 0); + let size = C_uint(bcx.ccx, nonnull.stride().bytes()); + let align = C_i32(bcx.ccx, nonnull.align.abi() as i32); + base::call_memset(bcx, llptr, fill_byte, size, align, false); } else { let path = discrfield.iter().map(|&i| i as usize).collect::<Vec<_>>(); - let llptrptr = GEPi(bcx, val, &path[..]); + let llptrptr = bcx.gepi(val, &path[..]); let llptrty = val_ty(llptrptr).element_type(); - Store(bcx, C_null(llptrty), llptrptr); + bcx.store(C_null(llptrty), llptrptr); } } } @@ -477,7 +484,7 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, } } -fn target_sets_discr_via_memset<'blk, 'tcx>(bcx: Block<'blk, 'tcx>) -> bool { +fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>) -> bool { bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64" } @@ -490,19 +497,15 @@ fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) { } /// Access a field, at a point when the value's case is known. -pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, - val: MaybeSizedValue, discr: Disr, ix: usize) -> ValueRef { - trans_field_ptr_builder(&bcx.build(), t, val, discr, ix) -} - -/// Access a field, at a point when the value's case is known. -pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, - t: Ty<'tcx>, - val: MaybeSizedValue, - discr: Disr, ix: usize) - -> ValueRef { - let l = bcx.ccx().layout_of(t); - debug!("trans_field_ptr_builder on {} represented as {:#?}", t, l); +pub fn trans_field_ptr<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + t: Ty<'tcx>, + val: MaybeSizedValue, + discr: Disr, + ix: usize +) -> ValueRef { + let l = bcx.ccx.layout_of(t); + debug!("trans_field_ptr on {} represented as {:#?}", t, l); // Note: if this ever needs to generate conditionals (e.g., if we // decide to do some kind of cdr-coding-like non-unique repr // someday), it will need to return a possibly-new bcx as well. @@ -510,7 +513,7 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, layout::Univariant { ref variant, .. } => { assert_eq!(discr, Disr(0)); struct_field_ptr(bcx, &variant, - &compute_fields(bcx.ccx(), t, 0, false), + &compute_fields(bcx.ccx, t, 0, false), val, ix, false) } layout::Vector { count, .. } => { @@ -519,61 +522,57 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, bcx.struct_gep(val.value, ix) } layout::General { discr: d, ref variants, .. } => { - let mut fields = compute_fields(bcx.ccx(), t, discr.0 as usize, false); - fields.insert(0, d.to_ty(&bcx.ccx().tcx(), false)); + let mut fields = compute_fields(bcx.ccx, t, discr.0 as usize, false); + fields.insert(0, d.to_ty(&bcx.ccx.tcx(), false)); struct_field_ptr(bcx, &variants[discr.0 as usize], &fields, val, ix + 1, true) } layout::UntaggedUnion { .. } => { - let fields = compute_fields(bcx.ccx(), t, 0, false); - let ty = type_of::in_memory_type_of(bcx.ccx(), fields[ix]); - if bcx.is_unreachable() { return C_undef(ty.ptr_to()); } + let fields = compute_fields(bcx.ccx, t, 0, false); + let ty = type_of::in_memory_type_of(bcx.ccx, fields[ix]); bcx.pointercast(val.value, ty.ptr_to()) } layout::RawNullablePointer { nndiscr, .. } | layout::StructWrappedNullablePointer { nndiscr, .. } if discr.0 != nndiscr => { - let nullfields = compute_fields(bcx.ccx(), t, (1-nndiscr) as usize, false); + let nullfields = compute_fields(bcx.ccx, t, (1-nndiscr) as usize, false); // The unit-like case might have a nonzero number of unit-like fields. // (e.d., Result of Either with (), as one side.) - let ty = type_of::type_of(bcx.ccx(), nullfields[ix]); - assert_eq!(machine::llsize_of_alloc(bcx.ccx(), ty), 0); - // The contents of memory at this pointer can't matter, but use - // the value that's "reasonable" in case of pointer comparison. - if bcx.is_unreachable() { return C_undef(ty.ptr_to()); } + let ty = type_of::type_of(bcx.ccx, nullfields[ix]); + assert_eq!(machine::llsize_of_alloc(bcx.ccx, ty), 0); bcx.pointercast(val.value, ty.ptr_to()) } layout::RawNullablePointer { nndiscr, .. } => { - let nnty = compute_fields(bcx.ccx(), t, nndiscr as usize, false)[0]; + let nnty = compute_fields(bcx.ccx, t, nndiscr as usize, false)[0]; assert_eq!(ix, 0); assert_eq!(discr.0, nndiscr); - let ty = type_of::type_of(bcx.ccx(), nnty); - if bcx.is_unreachable() { return C_undef(ty.ptr_to()); } + let ty = type_of::type_of(bcx.ccx, nnty); bcx.pointercast(val.value, ty.ptr_to()) } layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { assert_eq!(discr.0, nndiscr); struct_field_ptr(bcx, &nonnull, - &compute_fields(bcx.ccx(), t, discr.0 as usize, false), + &compute_fields(bcx.ccx, t, discr.0 as usize, false), val, ix, false) } _ => bug!("element access in type without elements: {} represented as {:#?}", t, l) } } -fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, - st: &layout::Struct, fields: &Vec<Ty<'tcx>>, val: MaybeSizedValue, - ix: usize, needs_cast: bool) -> ValueRef { - let ccx = bcx.ccx(); +fn struct_field_ptr<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + st: &layout::Struct, + fields: &Vec<Ty<'tcx>>, + val: MaybeSizedValue, + ix: usize, + needs_cast: bool +) -> ValueRef { let fty = fields[ix]; - let ll_fty = type_of::in_memory_type_of(bcx.ccx(), fty); - if bcx.is_unreachable() { - return C_undef(ll_fty.ptr_to()); - } + let ccx = bcx.ccx; let ptr_val = if needs_cast { - let fields = fields.iter().map(|&ty| { - type_of::in_memory_type_of(ccx, ty) + let fields = st.field_index_by_increasing_offset().map(|i| { + type_of::in_memory_type_of(ccx, fields[i]) }).collect::<Vec<_>>(); let real_ty = Type::struct_(ccx, &fields[..], st.packed); bcx.pointercast(val.value, real_ty.ptr_to()) @@ -585,15 +584,16 @@ fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, // * First field - Always aligned properly // * Packed struct - There is no alignment padding // * Field is sized - pointer is properly aligned already - if ix == 0 || st.packed || type_is_sized(bcx.tcx(), fty) { - return bcx.struct_gep(ptr_val, ix); + if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed || + bcx.ccx.shared().type_is_sized(fty) { + return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize); } // If the type of the last field is [T] or str, then we don't need to do // any adjusments match fty.sty { ty::TySlice(..) | ty::TyStr => { - return bcx.struct_gep(ptr_val, ix); + return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize); } _ => () } @@ -605,8 +605,6 @@ fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, return bcx.struct_gep(ptr_val, ix); } - let dbloc = DebugLoc::None; - // We need to get the pointer manually now. // We do this by casting to a *i8, then offsetting it by the appropriate amount. // We do this instead of, say, simply adjusting the pointer from the result of a GEP @@ -626,7 +624,7 @@ fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, let offset = st.offsets[ix].bytes(); - let unaligned_offset = C_uint(bcx.ccx(), offset); + let unaligned_offset = C_uint(bcx.ccx, offset); // Get the alignment of the field let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta); @@ -637,19 +635,18 @@ fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, // (unaligned offset + (align - 1)) & -align // Calculate offset - dbloc.apply(bcx.fcx()); - let align_sub_1 = bcx.sub(align, C_uint(bcx.ccx(), 1u64)); + let align_sub_1 = bcx.sub(align, C_uint(bcx.ccx, 1u64)); let offset = bcx.and(bcx.add(unaligned_offset, align_sub_1), bcx.neg(align)); debug!("struct_field_ptr: DST field offset: {:?}", Value(offset)); // Cast and adjust pointer - let byte_ptr = bcx.pointercast(ptr_val, Type::i8p(bcx.ccx())); + let byte_ptr = bcx.pointercast(ptr_val, Type::i8p(bcx.ccx)); let byte_ptr = bcx.gep(byte_ptr, &[offset]); // Finally, cast back to the type expected - let ll_fty = type_of::in_memory_type_of(bcx.ccx(), fty); + let ll_fty = type_of::in_memory_type_of(bcx.ccx, fty); debug!("struct_field_ptr: Field type is {:?}", ll_fty); bcx.pointercast(byte_ptr, ll_fty.ptr_to()) } @@ -755,8 +752,12 @@ fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // offset of current value let mut offset = 0; let mut cfields = Vec::new(); - let offsets = st.offsets.iter().map(|i| i.bytes()); - for (&val, target_offset) in vals.iter().zip(offsets) { + cfields.reserve(st.offsets.len()*2); + + let parts = st.field_index_by_increasing_offset().map(|i| { + (&vals[i], st.offsets[i].bytes()) + }); + for (&val, target_offset) in parts { if offset < target_offset { cfields.push(padding(ccx, target_offset - offset)); offset = target_offset; @@ -807,14 +808,11 @@ pub fn const_get_field<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, let l = ccx.layout_of(t); match *l { layout::CEnum { .. } => bug!("element access in C-like enum const"), - layout::Univariant { .. } | layout::Vector { .. } => const_struct_field(val, ix), + layout::Univariant { ref variant, .. } => { + const_struct_field(val, variant.memory_index[ix] as usize) + } + layout::Vector { .. } => const_struct_field(val, ix), layout::UntaggedUnion { .. } => const_struct_field(val, 0), - layout::General { .. } => const_struct_field(val, ix + 1), - layout::RawNullablePointer { .. } => { - assert_eq!(ix, 0); - val - }, - layout::StructWrappedNullablePointer{ .. } => const_struct_field(val, ix), _ => bug!("{} does not have fields.", t) } } diff --git a/src/librustc_trans/asm.rs b/src/librustc_trans/asm.rs index 665e12cbe87..d6385e1ca15 100644 --- a/src/librustc_trans/asm.rs +++ b/src/librustc_trans/asm.rs @@ -12,7 +12,6 @@ use llvm::{self, ValueRef}; use base; -use build::*; use common::*; use type_of; use type_::Type; @@ -25,10 +24,12 @@ use syntax::ast::AsmDialect; use libc::{c_uint, c_char}; // Take an inline assembly expression and splat it out via LLVM -pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - ia: &hir::InlineAsm, - outputs: Vec<(ValueRef, Ty<'tcx>)>, - mut inputs: Vec<ValueRef>) { +pub fn trans_inline_asm<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + ia: &hir::InlineAsm, + outputs: Vec<(ValueRef, Ty<'tcx>)>, + mut inputs: Vec<ValueRef> +) { let mut ext_constraints = vec![]; let mut output_types = vec![]; @@ -47,7 +48,7 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, if out.is_indirect { indirect_outputs.push(val.unwrap()); } else { - output_types.push(type_of::type_of(bcx.ccx(), ty)); + output_types.push(type_of::type_of(bcx.ccx, ty)); } } if !indirect_outputs.is_empty() { @@ -78,9 +79,9 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Depending on how many outputs we have, the return type is different let num_outputs = output_types.len(); let output_type = match num_outputs { - 0 => Type::void(bcx.ccx()), + 0 => Type::void(bcx.ccx), 1 => output_types[0], - _ => Type::struct_(bcx.ccx(), &output_types[..], false) + _ => Type::struct_(bcx.ccx, &output_types[..], false) }; let dialect = match ia.dialect { @@ -90,32 +91,33 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let asm = CString::new(ia.asm.as_str().as_bytes()).unwrap(); let constraint_cstr = CString::new(all_constraints).unwrap(); - let r = InlineAsmCall(bcx, - asm.as_ptr(), - constraint_cstr.as_ptr(), - &inputs, - output_type, - ia.volatile, - ia.alignstack, - dialect); + let r = bcx.inline_asm_call( + asm.as_ptr(), + constraint_cstr.as_ptr(), + &inputs, + output_type, + ia.volatile, + ia.alignstack, + dialect + ); // Again, based on how many outputs we have let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); for (i, (_, &(val, _))) in outputs.enumerate() { - let v = if num_outputs == 1 { r } else { ExtractValue(bcx, r, i) }; - Store(bcx, v, val); + let v = if num_outputs == 1 { r } else { bcx.extract_value(r, i) }; + bcx.store(v, val); } // Store expn_id in a metadata node so we can map LLVM errors // back to source locations. See #17552. unsafe { let key = "srcloc"; - let kind = llvm::LLVMGetMDKindIDInContext(bcx.ccx().llcx(), + let kind = llvm::LLVMGetMDKindIDInContext(bcx.ccx.llcx(), key.as_ptr() as *const c_char, key.len() as c_uint); - let val: llvm::ValueRef = C_i32(bcx.ccx(), ia.expn_id.into_u32() as i32); + let val: llvm::ValueRef = C_i32(bcx.ccx, ia.expn_id.into_u32() as i32); llvm::LLVMSetMetadata(r, kind, - llvm::LLVMMDNodeInContext(bcx.ccx().llcx(), &val, 1)); + llvm::LLVMMDNodeInContext(bcx.ccx.llcx(), &val, 1)); } } diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 040a05b69a3..a147b598940 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -207,7 +207,12 @@ impl<'a> Linker for GnuLinker<'a> { if self.sess.target.target.options.is_like_osx { self.cmd.args(&["-dynamiclib", "-Wl,-dylib"]); - if self.sess.opts.cg.rpath { + // Note that the `osx_rpath_install_name` option here is a hack + // purely to support rustbuild right now, we should get a more + // principled solution at some point to force the compiler to pass + // the right `-Wl,-install_name` with an `@rpath` in it. + if self.sess.opts.cg.rpath || + self.sess.opts.debugging_opts.osx_rpath_install_name { let mut v = OsString::from("-Wl,-install_name,@rpath/"); v.push(out_filename.file_name().unwrap()); self.cmd.arg(&v); diff --git a/src/librustc_trans/back/msvc/registry.rs b/src/librustc_trans/back/msvc/registry.rs index 44b161a7575..8242f53896a 100644 --- a/src/librustc_trans/back/msvc/registry.rs +++ b/src/librustc_trans/back/msvc/registry.rs @@ -12,7 +12,7 @@ use std::io; use std::ffi::{OsString, OsStr}; use std::os::windows::prelude::*; use std::ptr; -use libc::{c_void, c_long}; +use libc::c_long; pub type DWORD = u32; type LPCWSTR = *const u16; @@ -38,8 +38,6 @@ pub enum __HKEY__ {} pub type HKEY = *mut __HKEY__; pub type PHKEY = *mut HKEY; pub type REGSAM = DWORD; -pub type LPWSTR = *mut u16; -pub type PFILETIME = *mut c_void; #[link(name = "advapi32")] extern "system" { diff --git a/src/librustc_trans/back/symbol_names.rs b/src/librustc_trans/back/symbol_names.rs index ff40cfda5ff..938848054fe 100644 --- a/src/librustc_trans/back/symbol_names.rs +++ b/src/librustc_trans/back/symbol_names.rs @@ -99,8 +99,6 @@ use common::SharedCrateContext; use monomorphize::Instance; -use rustc_data_structures::fmt_wrap::FmtWrap; -use rustc_data_structures::blake2b::Blake2bHasher; use rustc::middle::weak_lang_items; use rustc::hir::def_id::LOCAL_CRATE; @@ -135,7 +133,7 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, let tcx = scx.tcx(); - let mut hasher = ty::util::TypeIdHasher::new(tcx, Blake2bHasher::new(8, &[])); + let mut hasher = ty::util::TypeIdHasher::<u64>::new(tcx); record_time(&tcx.sess.perf_stats.symbol_hash_time, || { // the main symbol name is not necessarily unique; hash in the @@ -158,9 +156,7 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, }); // 64 bits should be enough to avoid collisions. - let mut hasher = hasher.into_inner(); - let hash_bytes = hasher.finalize(); - format!("h{:x}", FmtWrap(hash_bytes)) + format!("h{:016x}", hasher.finish()) } impl<'a, 'tcx> Instance<'tcx> { diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index ffab0bde7ab..de8814f143e 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -1085,6 +1085,10 @@ fn run_work_multithreaded(sess: &Session, pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { let (pname, mut cmd, _) = get_linker(sess); + for arg in &sess.target.target.options.asm_args { + cmd.arg(arg); + } + cmd.arg("-c").arg("-o").arg(&outputs.path(OutputType::Object)) .arg(&outputs.temp_path(OutputType::Assembly, None)); debug!("{:?}", cmd); diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index f70c24c3ccb..76bb1c56af3 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -23,8 +23,6 @@ //! but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int, //! int) and rec(x=int, y=int, z=int) will have the same TypeRef. -#![allow(non_camel_case_types)] - use super::CrateTranslation; use super::ModuleLlvm; use super::ModuleSource; @@ -37,7 +35,7 @@ use back::symbol_export::{self, ExportedSymbols}; use llvm::{Linkage, ValueRef, Vector, get_param}; use llvm; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; -use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; +use middle::lang_items::StartFnLangItem; use rustc::ty::subst::Substs; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; @@ -51,20 +49,18 @@ use session::{self, DataTypeKind, Session}; use abi::{self, Abi, FnType}; use adt; use attributes; -use build::*; -use builder::{Builder, noname}; +use builder::Builder; use callee::{Callee}; -use common::{Block, C_bool, C_bytes_in_context, C_i32, C_uint}; +use common::{BlockAndBuilder, C_bool, C_bytes_in_context, C_i32, C_uint}; use collector::{self, TransItemCollectionMode}; -use common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef}; +use common::{C_struct_in_context, C_u64, C_undef}; use common::{CrateContext, FunctionContext}; -use common::{Result}; use common::{fulfill_obligation}; use common::{type_is_zero_size, val_ty}; use common; use consts; use context::{SharedCrateContext, CrateContextList}; -use debuginfo::{self, DebugLoc}; +use debuginfo; use declare; use machine; use machine::{llalign_of_min, llsize_of}; @@ -81,11 +77,8 @@ use value::Value; use Disr; use util::nodemap::{NodeSet, FxHashMap, FxHashSet}; -use arena::TypedArena; use libc::c_uint; use std::ffi::{CStr, CString}; -use std::cell::{Cell, RefCell}; -use std::ptr; use std::rc::Rc; use std::str; use std::i32; @@ -95,52 +88,6 @@ use rustc::hir; use rustc::ty::layout::{self, Layout}; use syntax::ast; -thread_local! { - static TASK_LOCAL_INSN_KEY: RefCell<Option<Vec<&'static str>>> = { - RefCell::new(None) - } -} - -pub fn with_insn_ctxt<F>(blk: F) - where F: FnOnce(&[&'static str]) -{ - TASK_LOCAL_INSN_KEY.with(move |slot| { - slot.borrow().as_ref().map(move |s| blk(s)); - }) -} - -pub fn init_insn_ctxt() { - TASK_LOCAL_INSN_KEY.with(|slot| { - *slot.borrow_mut() = Some(Vec::new()); - }); -} - -pub struct _InsnCtxt { - _cannot_construct_outside_of_this_module: (), -} - -impl Drop for _InsnCtxt { - fn drop(&mut self) { - TASK_LOCAL_INSN_KEY.with(|slot| { - if let Some(ctx) = slot.borrow_mut().as_mut() { - ctx.pop(); - } - }) - } -} - -pub fn push_ctxt(s: &'static str) -> _InsnCtxt { - debug!("new InsnCtxt: {}", s); - TASK_LOCAL_INSN_KEY.with(|slot| { - if let Some(ctx) = slot.borrow_mut().as_mut() { - ctx.push(s) - } - }); - _InsnCtxt { - _cannot_construct_outside_of_this_module: (), - } -} - pub struct StatRecorder<'a, 'tcx: 'a> { ccx: &'a CrateContext<'a, 'tcx>, name: Option<String>, @@ -162,10 +109,7 @@ impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { fn drop(&mut self) { if self.ccx.sess().trans_stats() { let iend = self.ccx.stats().n_llvm_insns.get(); - self.ccx - .stats() - .fn_stats - .borrow_mut() + self.ccx.stats().fn_stats.borrow_mut() .push((self.name.take().unwrap(), iend - self.istart)); self.ccx.stats().n_fns.set(self.ccx.stats().n_fns.get() + 1); // Reset LLVM insn count to avoid compound costs. @@ -174,52 +118,14 @@ impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { } } -pub fn get_meta(bcx: Block, fat_ptr: ValueRef) -> ValueRef { - StructGEP(bcx, fat_ptr, abi::FAT_PTR_EXTRA) -} - -pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef { - StructGEP(bcx, fat_ptr, abi::FAT_PTR_ADDR) +pub fn get_meta(bcx: &Builder, fat_ptr: ValueRef) -> ValueRef { + bcx.struct_gep(fat_ptr, abi::FAT_PTR_EXTRA) } -pub fn get_meta_builder(b: &Builder, fat_ptr: ValueRef) -> ValueRef { - b.struct_gep(fat_ptr, abi::FAT_PTR_EXTRA) +pub fn get_dataptr(bcx: &Builder, fat_ptr: ValueRef) -> ValueRef { + bcx.struct_gep(fat_ptr, abi::FAT_PTR_ADDR) } -pub fn get_dataptr_builder(b: &Builder, fat_ptr: ValueRef) -> ValueRef { - b.struct_gep(fat_ptr, abi::FAT_PTR_ADDR) -} - -fn require_alloc_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, info_ty: Ty<'tcx>, it: LangItem) -> DefId { - match bcx.tcx().lang_items.require(it) { - Ok(id) => id, - Err(s) => { - bcx.sess().fatal(&format!("allocation of `{}` {}", info_ty, s)); - } - } -} - -// The following malloc_raw_dyn* functions allocate a box to contain -// a given type, but with a potentially dynamic size. - -pub fn malloc_raw_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - llty_ptr: Type, - info_ty: Ty<'tcx>, - size: ValueRef, - align: ValueRef, - debug_loc: DebugLoc) - -> Result<'blk, 'tcx> { - let _icx = push_ctxt("malloc_raw_exchange"); - - // Allocate space: - let def_id = require_alloc_fn(bcx, info_ty, ExchangeMallocFnLangItem); - let r = Callee::def(bcx.ccx(), def_id, bcx.tcx().intern_substs(&[])) - .call(bcx, debug_loc, &[size, align], None); - - Result::new(r.bcx, PointerCast(r.bcx, r.val, llty_ptr)) -} - - pub fn bin_op_to_icmp_predicate(op: hir::BinOp_, signed: bool) -> llvm::IntPredicate { @@ -254,18 +160,18 @@ pub fn bin_op_to_fcmp_predicate(op: hir::BinOp_) -> llvm::RealPredicate { } } -pub fn compare_simd_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - t: Ty<'tcx>, - ret_ty: Type, - op: hir::BinOp_, - debug_loc: DebugLoc) - -> ValueRef { +pub fn compare_simd_types<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + t: Ty<'tcx>, + ret_ty: Type, + op: hir::BinOp_ +) -> ValueRef { let signed = match t.sty { ty::TyFloat(_) => { let cmp = bin_op_to_fcmp_predicate(op); - return SExt(bcx, FCmp(bcx, cmp, lhs, rhs, debug_loc), ret_ty); + return bcx.sext(bcx.fcmp(cmp, lhs, rhs), ret_ty); }, ty::TyUint(_) => false, ty::TyInt(_) => true, @@ -277,7 +183,7 @@ pub fn compare_simd_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // to get the correctly sized type. This will compile to a single instruction // once the IR is converted to assembly if the SIMD instruction is supported // by the target architecture. - SExt(bcx, ICmp(bcx, cmp, lhs, rhs, debug_loc), ret_ty) + bcx.sext(bcx.icmp(cmp, lhs, rhs), ret_ty) } /// Retrieve the information we are losing (making dynamic) in an unsizing @@ -311,11 +217,12 @@ pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>, } /// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. -pub fn unsize_thin_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - src: ValueRef, - src_ty: Ty<'tcx>, - dst_ty: Ty<'tcx>) - -> (ValueRef, ValueRef) { +pub fn unsize_thin_ptr<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + src: ValueRef, + src_ty: Ty<'tcx>, + dst_ty: Ty<'tcx> +) -> (ValueRef, ValueRef) { debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); match (&src_ty.sty, &dst_ty.sty) { (&ty::TyBox(a), &ty::TyBox(b)) | @@ -325,10 +232,9 @@ pub fn unsize_thin_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }), &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { - assert!(common::type_is_sized(bcx.tcx(), a)); - let ptr_ty = type_of::in_memory_type_of(bcx.ccx(), b).ptr_to(); - (PointerCast(bcx, src, ptr_ty), - unsized_info(bcx.ccx(), a, b, None)) + assert!(bcx.ccx.shared().type_is_sized(a)); + let ptr_ty = type_of::in_memory_type_of(bcx.ccx, b).ptr_to(); + (bcx.pointercast(src, ptr_ty), unsized_info(bcx.ccx, a, b, None)) } _ => bug!("unsize_thin_ptr: called on bad types"), } @@ -336,24 +242,24 @@ pub fn unsize_thin_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, /// Coerce `src`, which is a reference to a value of type `src_ty`, /// to a value of type `dst_ty` and store the result in `dst` -pub fn coerce_unsized_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - src: ValueRef, - src_ty: Ty<'tcx>, - dst: ValueRef, - dst_ty: Ty<'tcx>) { +pub fn coerce_unsized_into<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + src: ValueRef, + src_ty: Ty<'tcx>, + dst: ValueRef, + dst_ty: Ty<'tcx>) { match (&src_ty.sty, &dst_ty.sty) { (&ty::TyBox(..), &ty::TyBox(..)) | (&ty::TyRef(..), &ty::TyRef(..)) | (&ty::TyRef(..), &ty::TyRawPtr(..)) | (&ty::TyRawPtr(..), &ty::TyRawPtr(..)) => { - let (base, info) = if common::type_is_fat_ptr(bcx.tcx(), src_ty) { + let (base, info) = if common::type_is_fat_ptr(bcx.ccx, src_ty) { // fat-ptr to fat-ptr unsize preserves the vtable // i.e. &'a fmt::Debug+Send => &'a fmt::Debug // So we need to pointercast the base to ensure // the types match up. let (base, info) = load_fat_ptr(bcx, src, src_ty); - let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx(), dst_ty); - let base = PointerCast(bcx, base, llcast_ty); + let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx, dst_ty); + let base = bcx.pointercast(base, llcast_ty); (base, info) } else { let base = load_ty(bcx, src, src_ty); @@ -377,7 +283,7 @@ pub fn coerce_unsized_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_fty, dst_fty)) in iter { - if type_is_zero_size(bcx.ccx(), dst_fty) { + if type_is_zero_size(bcx.ccx, dst_fty) { continue; } @@ -415,8 +321,10 @@ pub fn custom_coerce_unsize_info<'scx, 'tcx>(scx: &SharedCrateContext<'scx, 'tcx } } -pub fn cast_shift_expr_rhs(cx: Block, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - cast_shift_rhs(op, lhs, rhs, |a, b| Trunc(cx, a, b), |a, b| ZExt(cx, a, b)) +pub fn cast_shift_expr_rhs( + cx: &BlockAndBuilder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef +) -> ValueRef { + cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b)) } pub fn cast_shift_const_rhs(op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef) -> ValueRef { @@ -462,42 +370,6 @@ fn cast_shift_rhs<F, G>(op: hir::BinOp_, } } -pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - llfn: ValueRef, - llargs: &[ValueRef], - debug_loc: DebugLoc) - -> (ValueRef, Block<'blk, 'tcx>) { - let _icx = push_ctxt("invoke_"); - if bcx.unreachable.get() { - return (C_null(Type::i8(bcx.ccx())), bcx); - } - - if need_invoke(bcx) { - debug!("invoking {:?} at {:?}", Value(llfn), bcx.llbb); - for &llarg in llargs { - debug!("arg: {:?}", Value(llarg)); - } - let normal_bcx = bcx.fcx.new_block("normal-return"); - let landing_pad = bcx.fcx.get_landing_pad(); - - let llresult = Invoke(bcx, - llfn, - &llargs[..], - normal_bcx.llbb, - landing_pad, - debug_loc); - return (llresult, normal_bcx); - } else { - debug!("calling {:?} at {:?}", Value(llfn), bcx.llbb); - for &llarg in llargs { - debug!("arg: {:?}", Value(llarg)); - } - - let llresult = Call(bcx, llfn, &llargs[..], debug_loc); - return (llresult, bcx); - } -} - /// Returns whether this session's target will use SEH-based unwinding. /// /// This is only true for MSVC targets, and even then the 64-bit MSVC target @@ -507,18 +379,6 @@ pub fn wants_msvc_seh(sess: &Session) -> bool { sess.target.target.options.is_like_msvc } -pub fn avoid_invoke(bcx: Block) -> bool { - bcx.sess().no_landing_pads() || bcx.lpad().is_some() -} - -pub fn need_invoke(bcx: Block) -> bool { - if avoid_invoke(bcx) { - false - } else { - bcx.fcx.needs_invoke() - } -} - pub fn call_assume<'a, 'tcx>(b: &Builder<'a, 'tcx>, val: ValueRef) { let assume_intrinsic = b.ccx.get_intrinsic("llvm.assume"); b.call(assume_intrinsic, &[val], None); @@ -527,14 +387,7 @@ pub fn call_assume<'a, 'tcx>(b: &Builder<'a, 'tcx>, val: ValueRef) { /// Helper for loading values from memory. Does the necessary conversion if the in-memory type /// differs from the type used for SSA values. Also handles various special cases where the type /// gives us better information about what we are loading. -pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> ValueRef { - if cx.unreachable.get() { - return C_undef(type_of::type_of(cx.ccx(), t)); - } - load_ty_builder(&B(cx), ptr, t) -} - -pub fn load_ty_builder<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> ValueRef { +pub fn load_ty<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> ValueRef { let ccx = b.ccx; if type_is_zero_size(ccx, t) { return C_undef(type_of::type_of(ccx, t)); @@ -559,8 +412,7 @@ pub fn load_ty_builder<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, t: Ty<'tc // a char is a Unicode codepoint, and so takes values from 0 // to 0x10FFFF inclusive only. b.load_range_assert(ptr, 0, 0x10FFFF + 1, llvm::False) - } else if (t.is_region_ptr() || t.is_unique()) && - !common::type_is_fat_ptr(ccx.tcx(), t) { + } else if (t.is_region_ptr() || t.is_unique()) && !common::type_is_fat_ptr(ccx, t) { b.load_nonnull(ptr) } else { b.load(ptr) @@ -569,54 +421,32 @@ pub fn load_ty_builder<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, t: Ty<'tc /// Helper for storing values in memory. Does the necessary conversion if the in-memory type /// differs from the type used for SSA values. -pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t: Ty<'tcx>) { - if cx.unreachable.get() { - return; - } - +pub fn store_ty<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>, v: ValueRef, dst: ValueRef, t: Ty<'tcx>) { debug!("store_ty: {:?} : {:?} <- {:?}", Value(dst), t, Value(v)); - if common::type_is_fat_ptr(cx.tcx(), t) { - let lladdr = ExtractValue(cx, v, abi::FAT_PTR_ADDR); - let llextra = ExtractValue(cx, v, abi::FAT_PTR_EXTRA); + if common::type_is_fat_ptr(cx.ccx, t) { + let lladdr = cx.extract_value(v, abi::FAT_PTR_ADDR); + let llextra = cx.extract_value(v, abi::FAT_PTR_EXTRA); store_fat_ptr(cx, lladdr, llextra, dst, t); } else { - Store(cx, from_immediate(cx, v), dst); + cx.store(from_immediate(cx, v), dst); } } -pub fn store_fat_ptr<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - data: ValueRef, - extra: ValueRef, - dst: ValueRef, - _ty: Ty<'tcx>) { +pub fn store_fat_ptr<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>, + data: ValueRef, + extra: ValueRef, + dst: ValueRef, + _ty: Ty<'tcx>) { // FIXME: emit metadata - Store(cx, data, get_dataptr(cx, dst)); - Store(cx, extra, get_meta(cx, dst)); + cx.store(data, get_dataptr(cx, dst)); + cx.store(extra, get_meta(cx, dst)); } -pub fn load_fat_ptr<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - src: ValueRef, - ty: Ty<'tcx>) - -> (ValueRef, ValueRef) -{ - if cx.unreachable.get() { - // FIXME: remove me - return (Load(cx, get_dataptr(cx, src)), - Load(cx, get_meta(cx, src))); - } - - load_fat_ptr_builder(&B(cx), src, ty) -} - -pub fn load_fat_ptr_builder<'a, 'tcx>( - b: &Builder<'a, 'tcx>, - src: ValueRef, - t: Ty<'tcx>) - -> (ValueRef, ValueRef) -{ - - let ptr = get_dataptr_builder(b, src); +pub fn load_fat_ptr<'a, 'tcx>( + b: &Builder<'a, 'tcx>, src: ValueRef, t: Ty<'tcx> +) -> (ValueRef, ValueRef) { + let ptr = get_dataptr(b, src); let ptr = if t.is_region_ptr() || t.is_unique() { b.load_nonnull(ptr) } else { @@ -624,122 +454,63 @@ pub fn load_fat_ptr_builder<'a, 'tcx>( }; // FIXME: emit metadata on `meta`. - let meta = b.load(get_meta_builder(b, src)); + let meta = b.load(get_meta(b, src)); (ptr, meta) } -pub fn from_immediate(bcx: Block, val: ValueRef) -> ValueRef { - if val_ty(val) == Type::i1(bcx.ccx()) { - ZExt(bcx, val, Type::i8(bcx.ccx())) +pub fn from_immediate(bcx: &BlockAndBuilder, val: ValueRef) -> ValueRef { + if val_ty(val) == Type::i1(bcx.ccx) { + bcx.zext(val, Type::i8(bcx.ccx)) } else { val } } -pub fn to_immediate(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef { +pub fn to_immediate(bcx: &BlockAndBuilder, val: ValueRef, ty: Ty) -> ValueRef { if ty.is_bool() { - Trunc(bcx, val, Type::i1(bcx.ccx())) + bcx.trunc(val, Type::i1(bcx.ccx)) } else { val } } -pub fn with_cond<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, val: ValueRef, f: F) -> Block<'blk, 'tcx> - where F: FnOnce(Block<'blk, 'tcx>) -> Block<'blk, 'tcx> -{ - let _icx = push_ctxt("with_cond"); - - if bcx.unreachable.get() || common::const_to_opt_uint(val) == Some(0) { - return bcx; - } - - let fcx = bcx.fcx; - let next_cx = fcx.new_block("next"); - let cond_cx = fcx.new_block("cond"); - CondBr(bcx, val, cond_cx.llbb, next_cx.llbb, DebugLoc::None); - let after_cx = f(cond_cx); - if !after_cx.terminated.get() { - Br(after_cx, next_cx.llbb, DebugLoc::None); - } - next_cx -} - pub enum Lifetime { Start, End } -// If LLVM lifetime intrinsic support is enabled (i.e. optimizations -// 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. -// -// If LLVM lifetime intrinsic support is disabled (i.e. optimizations -// off) or `ptr` is zero-sized, then no-op (does not call `emit`). -fn core_lifetime_emit<'blk, 'tcx, F>(ccx: &'blk CrateContext<'blk, 'tcx>, - ptr: ValueRef, - lt: Lifetime, - emit: F) - where F: FnOnce(&'blk CrateContext<'blk, 'tcx>, machine::llsize, ValueRef) -{ - if ccx.sess().opts.optimize == config::OptLevel::No { - return; - } - - let _icx = push_ctxt(match lt { - Lifetime::Start => "lifetime_start", - Lifetime::End => "lifetime_end" - }); - - let size = machine::llsize_of_alloc(ccx, val_ty(ptr).element_type()); - if size == 0 { - return; - } - - let lifetime_intrinsic = ccx.get_intrinsic(match lt { - Lifetime::Start => "llvm.lifetime.start", - Lifetime::End => "llvm.lifetime.end" - }); - emit(ccx, size, lifetime_intrinsic) -} - impl Lifetime { + // If LLVM lifetime intrinsic support is enabled (i.e. optimizations + // 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. + // + // If LLVM lifetime intrinsic support is disabled (i.e. optimizations + // off) or `ptr` is zero-sized, then no-op (does not call `emit`). pub fn call(self, b: &Builder, ptr: ValueRef) { - core_lifetime_emit(b.ccx, ptr, self, |ccx, size, lifetime_intrinsic| { - let ptr = b.pointercast(ptr, Type::i8p(ccx)); - b.call(lifetime_intrinsic, &[C_u64(ccx, size), ptr], None); - }); - } -} + if b.ccx.sess().opts.optimize == config::OptLevel::No { + return; + } -pub fn call_lifetime_start(bcx: Block, ptr: ValueRef) { - if !bcx.unreachable.get() { - Lifetime::Start.call(&bcx.build(), ptr); - } -} + let size = machine::llsize_of_alloc(b.ccx, val_ty(ptr).element_type()); + if size == 0 { + return; + } -pub fn call_lifetime_end(bcx: Block, ptr: ValueRef) { - if !bcx.unreachable.get() { - Lifetime::End.call(&bcx.build(), ptr); - } -} + let lifetime_intrinsic = b.ccx.get_intrinsic(match self { + Lifetime::Start => "llvm.lifetime.start", + Lifetime::End => "llvm.lifetime.end" + }); -// Generates code for resumption of unwind at the end of a landing pad. -pub fn trans_unwind_resume(bcx: Block, lpval: ValueRef) { - if !bcx.sess().target.target.options.custom_unwind_resume { - Resume(bcx, lpval); - } else { - let exc_ptr = ExtractValue(bcx, lpval, 0); - bcx.fcx.eh_unwind_resume() - .call(bcx, DebugLoc::None, &[exc_ptr], None); + let ptr = b.pointercast(ptr, Type::i8p(b.ccx)); + b.call(lifetime_intrinsic, &[C_u64(b.ccx, size), ptr], None); } } -pub fn call_memcpy<'bcx, 'tcx>(b: &Builder<'bcx, 'tcx>, +pub fn call_memcpy<'a, 'tcx>(b: &Builder<'a, 'tcx>, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) { - let _icx = push_ctxt("call_memcpy"); let ccx = b.ccx; let ptr_width = &ccx.sess().target.target.target_pointer_width[..]; let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width); @@ -752,11 +523,12 @@ pub fn call_memcpy<'bcx, 'tcx>(b: &Builder<'bcx, 'tcx>, b.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); } -pub fn memcpy_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, dst: ValueRef, src: ValueRef, t: Ty<'tcx>) { - let _icx = push_ctxt("memcpy_ty"); - let ccx = bcx.ccx(); +pub fn memcpy_ty<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, dst: ValueRef, src: ValueRef, t: Ty<'tcx> +) { + let ccx = bcx.ccx; - if type_is_zero_size(ccx, t) || bcx.unreachable.get() { + if type_is_zero_size(ccx, t) { return; } @@ -764,8 +536,8 @@ pub fn memcpy_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, dst: ValueRef, src: ValueRe let llty = type_of::type_of(ccx, t); let llsz = llsize_of(ccx, llty); let llalign = type_of::align_of(ccx, t); - call_memcpy(&B(bcx), dst, src, llsz, llalign as u32); - } else if common::type_is_fat_ptr(bcx.tcx(), t) { + call_memcpy(bcx, dst, src, llsz, llalign as u32); + } else if common::type_is_fat_ptr(bcx.ccx, t) { let (data, extra) = load_fat_ptr(bcx, src, t); store_fat_ptr(bcx, data, extra, dst, t); } else { @@ -773,232 +545,22 @@ pub fn memcpy_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, dst: ValueRef, src: ValueRe } } -pub fn init_zero_mem<'blk, 'tcx>(cx: Block<'blk, 'tcx>, llptr: ValueRef, t: Ty<'tcx>) { - if cx.unreachable.get() { - return; - } - let _icx = push_ctxt("init_zero_mem"); - let bcx = cx; - memfill(&B(bcx), llptr, t, 0); -} - -// Always use this function instead of storing a constant byte to the memory -// in question. e.g. if you store a zero constant, LLVM will drown in vreg -// allocation for large data structures, and the generated code will be -// awful. (A telltale sign of this is large quantities of -// `mov [byte ptr foo],0` in the generated code.) -fn memfill<'a, 'tcx>(b: &Builder<'a, 'tcx>, llptr: ValueRef, ty: Ty<'tcx>, byte: u8) { - let _icx = push_ctxt("memfill"); - let ccx = b.ccx; - let llty = type_of::type_of(ccx, ty); - let llptr = b.pointercast(llptr, Type::i8(ccx).ptr_to()); - let llzeroval = C_u8(ccx, byte); - let size = machine::llsize_of(ccx, llty); - let align = C_i32(ccx, type_of::align_of(ccx, ty) as i32); - call_memset(b, llptr, llzeroval, size, align, false); -} - -pub fn call_memset<'bcx, 'tcx>(b: &Builder<'bcx, 'tcx>, +pub fn call_memset<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, fill_byte: ValueRef, size: ValueRef, align: ValueRef, - volatile: bool) { - let ccx = b.ccx; - let ptr_width = &ccx.sess().target.target.target_pointer_width[..]; + volatile: bool) -> ValueRef { + let ptr_width = &b.ccx.sess().target.target.target_pointer_width[..]; let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); - let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key); - let volatile = C_bool(ccx, volatile); - b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None); + let llintrinsicfn = b.ccx.get_intrinsic(&intrinsic_key); + let volatile = C_bool(b.ccx, volatile); + b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None) } -pub fn alloc_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - ty: Ty<'tcx>, - name: &str) -> ValueRef { +pub fn alloc_ty<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> ValueRef { assert!(!ty.has_param_types()); - alloca(bcx, type_of::type_of(bcx.ccx(), ty), name) -} - -pub fn alloca(cx: Block, ty: Type, name: &str) -> ValueRef { - let _icx = push_ctxt("alloca"); - if cx.unreachable.get() { - unsafe { - return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); - } - } - DebugLoc::None.apply(cx.fcx); - Alloca(cx, ty, name) -} - -impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> { - /// Create a function context for the given function. - /// Beware that you must call `fcx.init` or `fcx.bind_args` - /// before doing anything with the returned function context. - pub fn new(ccx: &'blk CrateContext<'blk, 'tcx>, - llfndecl: ValueRef, - fn_ty: FnType, - definition: Option<(Instance<'tcx>, &ty::FnSig<'tcx>, Abi)>, - block_arena: &'blk TypedArena<common::BlockS<'blk, 'tcx>>) - -> FunctionContext<'blk, 'tcx> { - let (param_substs, def_id) = match definition { - Some((instance, ..)) => { - common::validate_substs(instance.substs); - (instance.substs, Some(instance.def)) - } - None => (ccx.tcx().intern_substs(&[]), None) - }; - - let local_id = def_id.and_then(|id| ccx.tcx().map.as_local_node_id(id)); - - debug!("FunctionContext::new({})", - definition.map_or(String::new(), |d| d.0.to_string())); - - let no_debug = if let Some(id) = local_id { - ccx.tcx().map.attrs(id) - .iter().any(|item| item.check_name("no_debug")) - } else if let Some(def_id) = def_id { - ccx.sess().cstore.item_attrs(def_id) - .iter().any(|item| item.check_name("no_debug")) - } else { - false - }; - - let mir = def_id.map(|id| ccx.tcx().item_mir(id)); - - let debug_context = if let (false, Some((instance, sig, abi)), &Some(ref mir)) = - (no_debug, definition, &mir) { - debuginfo::create_function_debug_context(ccx, instance, sig, abi, llfndecl, mir) - } else { - debuginfo::empty_function_debug_context(ccx) - }; - - FunctionContext { - mir: mir, - llfn: llfndecl, - llretslotptr: Cell::new(None), - param_env: ccx.tcx().empty_parameter_environment(), - alloca_insert_pt: Cell::new(None), - landingpad_alloca: Cell::new(None), - fn_ty: fn_ty, - param_substs: param_substs, - span: None, - block_arena: block_arena, - lpad_arena: TypedArena::new(), - ccx: ccx, - debug_context: debug_context, - scopes: RefCell::new(Vec::new()), - } - } - - /// Performs setup on a newly created function, creating the entry - /// scope block and allocating space for the return pointer. - pub fn init(&'blk self, skip_retptr: bool) -> Block<'blk, 'tcx> { - let entry_bcx = self.new_block("entry-block"); - - // Use a dummy instruction as the insertion point for all allocas. - // This is later removed in FunctionContext::cleanup. - self.alloca_insert_pt.set(Some(unsafe { - Load(entry_bcx, C_null(Type::i8p(self.ccx))); - llvm::LLVMGetFirstInstruction(entry_bcx.llbb) - })); - - if !self.fn_ty.ret.is_ignore() && !skip_retptr { - // We normally allocate the llretslotptr, unless we - // have been instructed to skip it for immediate return - // values, or there is nothing to return at all. - - // We create an alloca to hold a pointer of type `ret.original_ty` - // which will hold the pointer to the right alloca which has the - // final ret value - let llty = self.fn_ty.ret.memory_ty(self.ccx); - // But if there are no nested returns, we skip the indirection - // and have a single retslot - let slot = if self.fn_ty.ret.is_indirect() { - get_param(self.llfn, 0) - } else { - AllocaFcx(self, llty, "sret_slot") - }; - - self.llretslotptr.set(Some(slot)); - } - - entry_bcx - } - - /// Ties up the llstaticallocas -> llloadenv -> lltop edges, - /// and builds the return block. - pub fn finish(&'blk self, ret_cx: Block<'blk, 'tcx>, - ret_debug_loc: DebugLoc) { - let _icx = push_ctxt("FunctionContext::finish"); - - self.build_return_block(ret_cx, ret_debug_loc); - - DebugLoc::None.apply(self); - self.cleanup(); - } - - // Builds the return block for a function. - pub fn build_return_block(&self, ret_cx: Block<'blk, 'tcx>, - ret_debug_location: DebugLoc) { - if self.llretslotptr.get().is_none() || - ret_cx.unreachable.get() || - self.fn_ty.ret.is_indirect() { - return RetVoid(ret_cx, ret_debug_location); - } - - let retslot = self.llretslotptr.get().unwrap(); - let retptr = Value(retslot); - let llty = self.fn_ty.ret.original_ty; - match (retptr.get_dominating_store(ret_cx), self.fn_ty.ret.cast) { - // If there's only a single store to the ret slot, we can directly return - // the value that was stored and omit the store and the alloca. - // However, we only want to do this when there is no cast needed. - (Some(s), None) => { - let mut retval = s.get_operand(0).unwrap().get(); - s.erase_from_parent(); - - if retptr.has_no_uses() { - retptr.erase_from_parent(); - } - - if self.fn_ty.ret.is_indirect() { - Store(ret_cx, retval, get_param(self.llfn, 0)); - RetVoid(ret_cx, ret_debug_location) - } else { - if llty == Type::i1(self.ccx) { - retval = Trunc(ret_cx, retval, llty); - } - Ret(ret_cx, retval, ret_debug_location) - } - } - (_, cast_ty) if self.fn_ty.ret.is_indirect() => { - // Otherwise, copy the return value to the ret slot. - assert_eq!(cast_ty, None); - let llsz = llsize_of(self.ccx, self.fn_ty.ret.ty); - let llalign = llalign_of_min(self.ccx, self.fn_ty.ret.ty); - call_memcpy(&B(ret_cx), get_param(self.llfn, 0), - retslot, llsz, llalign as u32); - RetVoid(ret_cx, ret_debug_location) - } - (_, Some(cast_ty)) => { - let load = Load(ret_cx, PointerCast(ret_cx, retslot, cast_ty.ptr_to())); - let llalign = llalign_of_min(self.ccx, self.fn_ty.ret.ty); - unsafe { - llvm::LLVMSetAlignment(load, llalign); - } - Ret(ret_cx, load, ret_debug_location) - } - (_, None) => { - let retval = if llty == Type::i1(self.ccx) { - let val = LoadRangeAssert(ret_cx, retslot, 0, 2, llvm::False); - Trunc(ret_cx, val, llty) - } else { - Load(ret_cx, retslot) - }; - Ret(ret_cx, retval, ret_debug_location) - } - } - } + bcx.fcx().alloca(type_of::type_of(bcx.ccx, ty), name) } pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) { @@ -1016,8 +578,6 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance // release builds. info!("trans_instance({})", instance); - let _icx = push_ctxt("trans_instance"); - let fn_ty = ccx.tcx().item_type(instance.def); let fn_ty = ccx.tcx().erase_regions(&fn_ty); let fn_ty = monomorphize::apply_param_substs(ccx.shared(), instance.substs, &fn_ty); @@ -1038,19 +598,9 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance let fn_ty = FnType::new(ccx, abi, &sig, &[]); - let (arena, fcx): (TypedArena<_>, FunctionContext); - arena = TypedArena::new(); - fcx = FunctionContext::new(ccx, - lldecl, - fn_ty, - Some((instance, &sig, abi)), - &arena); - - if fcx.mir.is_none() { - bug!("attempted translation of `{}` w/o MIR", instance); - } - - mir::trans_mir(&fcx); + let fcx = FunctionContext::new(ccx, lldecl); + let mir = ccx.tcx().item_mir(instance.def); + mir::trans_mir(&fcx, fn_ty, &mir, instance, &sig, abi); } pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, @@ -1067,34 +617,55 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&ctor_ty.fn_sig()); let fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]); - let (arena, fcx): (TypedArena<_>, FunctionContext); - arena = TypedArena::new(); - fcx = FunctionContext::new(ccx, llfndecl, fn_ty, None, &arena); - let bcx = fcx.init(false); - - if !fcx.fn_ty.ret.is_ignore() { - let dest = fcx.llretslotptr.get().unwrap(); + let fcx = FunctionContext::new(ccx, llfndecl); + let bcx = fcx.get_entry_block(); + if !fn_ty.ret.is_ignore() { + // But if there are no nested returns, we skip the indirection + // and have a single retslot + let dest = if fn_ty.ret.is_indirect() { + get_param(fcx.llfn, 0) + } else { + // We create an alloca to hold a pointer of type `ret.original_ty` + // which will hold the pointer to the right alloca which has the + // final ret value + fcx.alloca(fn_ty.ret.memory_ty(ccx), "sret_slot") + }; let dest_val = adt::MaybeSizedValue::sized(dest); // Can return unsized value - let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize; + let mut llarg_idx = fn_ty.ret.is_indirect() as usize; let mut arg_idx = 0; for (i, arg_ty) in sig.inputs().iter().enumerate() { - let lldestptr = adt::trans_field_ptr(bcx, sig.output(), dest_val, Disr::from(disr), i); - let arg = &fcx.fn_ty.args[arg_idx]; + let lldestptr = adt::trans_field_ptr(&bcx, sig.output(), dest_val, Disr::from(disr), i); + let arg = &fn_ty.args[arg_idx]; arg_idx += 1; - let b = &bcx.build(); - if common::type_is_fat_ptr(bcx.tcx(), arg_ty) { - let meta = &fcx.fn_ty.args[arg_idx]; + if common::type_is_fat_ptr(bcx.ccx, arg_ty) { + let meta = &fn_ty.args[arg_idx]; arg_idx += 1; - arg.store_fn_arg(b, &mut llarg_idx, get_dataptr(bcx, lldestptr)); - meta.store_fn_arg(b, &mut llarg_idx, get_meta(bcx, lldestptr)); + arg.store_fn_arg(&bcx, &mut llarg_idx, get_dataptr(&bcx, lldestptr)); + meta.store_fn_arg(&bcx, &mut llarg_idx, get_meta(&bcx, lldestptr)); } else { - arg.store_fn_arg(b, &mut llarg_idx, lldestptr); + arg.store_fn_arg(&bcx, &mut llarg_idx, lldestptr); } } - adt::trans_set_discr(bcx, sig.output(), dest, disr); - } + adt::trans_set_discr(&bcx, sig.output(), dest, disr); - fcx.finish(bcx, DebugLoc::None); + if fn_ty.ret.is_indirect() { + bcx.ret_void(); + return; + } + + if let Some(cast_ty) = fn_ty.ret.cast { + let load = bcx.load(bcx.pointercast(dest, cast_ty.ptr_to())); + let llalign = llalign_of_min(ccx, fn_ty.ret.ty); + unsafe { + llvm::LLVMSetAlignment(load, llalign); + } + bcx.ret(load) + } else { + bcx.ret(bcx.load(dest)) + } + } else { + bcx.ret_void(); + } } pub fn llvm_linkage_by_name(name: &str) -> Option<Linkage> { @@ -1166,9 +737,7 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { let et = ccx.sess().entry_type.get().unwrap(); match et { - config::EntryMain => { - create_entry_fn(ccx, span, main_llfn, true); - } + config::EntryMain => create_entry_fn(ccx, span, main_llfn, true), config::EntryStart => create_entry_fn(ccx, span, main_llfn, false), config::EntryNone => {} // Do nothing. } @@ -1193,47 +762,27 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { attributes::set_frame_pointer_elimination(ccx, llfn); let llbb = unsafe { - llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, "top\0".as_ptr() as *const _) + let name = CString::new("top").unwrap(); + llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, name.as_ptr()) }; - let bld = ccx.raw_builder(); - unsafe { - llvm::LLVMPositionBuilderAtEnd(bld, llbb); - - debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(ccx); - - let (start_fn, args) = if use_start_lang_item { - let start_def_id = match ccx.tcx().lang_items.require(StartFnLangItem) { - Ok(id) => id, - Err(s) => ccx.sess().fatal(&s) - }; - let empty_substs = ccx.tcx().intern_substs(&[]); - let start_fn = Callee::def(ccx, start_def_id, empty_substs).reify(ccx); - let args = { - let opaque_rust_main = - llvm::LLVMBuildPointerCast(bld, - rust_main, - Type::i8p(ccx).to_ref(), - "rust_main\0".as_ptr() as *const _); - - vec![opaque_rust_main, get_param(llfn, 0), get_param(llfn, 1)] - }; - (start_fn, args) - } else { - debug!("using user-defined start fn"); - let args = vec![get_param(llfn, 0 as c_uint), get_param(llfn, 1 as c_uint)]; + let bld = Builder::with_ccx(ccx); + bld.position_at_end(llbb); - (rust_main, args) - }; + debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(ccx, &bld); - let result = llvm::LLVMRustBuildCall(bld, - start_fn, - args.as_ptr(), - args.len() as c_uint, - ptr::null_mut(), - noname()); + let (start_fn, args) = if use_start_lang_item { + let start_def_id = ccx.tcx().require_lang_item(StartFnLangItem); + let empty_substs = ccx.tcx().intern_substs(&[]); + let start_fn = Callee::def(ccx, start_def_id, empty_substs).reify(ccx); + (start_fn, vec![bld.pointercast(rust_main, Type::i8p(ccx).ptr_to()), get_param(llfn, 0), + get_param(llfn, 1)]) + } else { + debug!("using user-defined start fn"); + (rust_main, vec![get_param(llfn, 0 as c_uint), get_param(llfn, 1 as c_uint)]) + }; - llvm::LLVMBuildRet(bld, result); - } + let result = bld.call(start_fn, &args, None); + bld.ret(result); } } @@ -1868,7 +1417,8 @@ fn gather_type_sizes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { match **layout { Layout::StructWrappedNullablePointer { nonnull: ref variant_layout, nndiscr, - discrfield: _ } => { + discrfield: _, + discrfield_source: _ } => { debug!("print-type-size t: `{:?}` adt struct-wrapped nullable nndiscr {} is {:?}", ty, nndiscr, variant_layout); let variant_def = &adt_def.variants[nndiscr as usize]; diff --git a/src/librustc_trans/basic_block.rs b/src/librustc_trans/basic_block.rs deleted file mode 100644 index 60bd3fb8ef1..00000000000 --- a/src/librustc_trans/basic_block.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm; -use llvm::BasicBlockRef; -use value::{Users, Value}; -use std::iter::{Filter, Map}; - -#[derive(Copy, Clone)] -pub struct BasicBlock(pub BasicBlockRef); - -pub type Preds = Map<Filter<Users, fn(&Value) -> bool>, fn(Value) -> BasicBlock>; - -/// Wrapper for LLVM BasicBlockRef -impl BasicBlock { - pub fn get(&self) -> BasicBlockRef { - let BasicBlock(v) = *self; v - } - - pub fn as_value(self) -> Value { - unsafe { - Value(llvm::LLVMBasicBlockAsValue(self.get())) - } - } - - pub fn pred_iter(self) -> Preds { - fn is_a_terminator_inst(user: &Value) -> bool { user.is_a_terminator_inst() } - let is_a_terminator_inst: fn(&Value) -> bool = is_a_terminator_inst; - - fn get_parent(user: Value) -> BasicBlock { user.get_parent().unwrap() } - let get_parent: fn(Value) -> BasicBlock = get_parent; - - self.as_value().user_iter() - .filter(is_a_terminator_inst) - .map(get_parent) - } - - pub fn get_single_predecessor(self) -> Option<BasicBlock> { - let mut iter = self.pred_iter(); - match (iter.next(), iter.next()) { - (Some(first), None) => Some(first), - _ => None - } - } - - pub fn delete(self) { - unsafe { - llvm::LLVMDeleteBasicBlock(self.0); - } - } -} diff --git a/src/librustc_trans/build.rs b/src/librustc_trans/build.rs deleted file mode 100644 index 8cd47bd148d..00000000000 --- a/src/librustc_trans/build.rs +++ /dev/null @@ -1,1167 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(dead_code)] // FFI wrappers -#![allow(non_snake_case)] - -use llvm; -use llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; -use llvm::{Opcode, IntPredicate, RealPredicate}; -use llvm::{ValueRef, BasicBlockRef}; -use common::*; -use syntax_pos::Span; - -use builder::Builder; -use type_::Type; -use value::Value; -use debuginfo::DebugLoc; - -use libc::{c_uint, c_char}; - -pub fn terminate(cx: Block, _: &str) { - debug!("terminate({})", cx.to_str()); - cx.terminated.set(true); -} - -pub fn check_not_terminated(cx: Block) { - if cx.terminated.get() { - bug!("already terminated!"); - } -} - -pub fn B<'blk, 'tcx>(cx: Block<'blk, 'tcx>) -> Builder<'blk, 'tcx> { - let b = cx.fcx.ccx.builder(); - b.position_at_end(cx.llbb); - b -} - -// The difference between a block being unreachable and being terminated is -// somewhat obscure, and has to do with error checking. When a block is -// terminated, we're saying that trying to add any further statements in the -// block is an error. On the other hand, if something is unreachable, that -// means that the block was terminated in some way that we don't want to check -// for (panic/break/return statements, call to diverging functions, etc), and -// further instructions to the block should simply be ignored. - -pub fn RetVoid(cx: Block, debug_loc: DebugLoc) { - if cx.unreachable.get() { - return; - } - check_not_terminated(cx); - terminate(cx, "RetVoid"); - debug_loc.apply(cx.fcx); - B(cx).ret_void(); -} - -pub fn Ret(cx: Block, v: ValueRef, debug_loc: DebugLoc) { - if cx.unreachable.get() { - return; - } - check_not_terminated(cx); - terminate(cx, "Ret"); - debug_loc.apply(cx.fcx); - B(cx).ret(v); -} - -pub fn AggregateRet(cx: Block, - ret_vals: &[ValueRef], - debug_loc: DebugLoc) { - if cx.unreachable.get() { - return; - } - check_not_terminated(cx); - terminate(cx, "AggregateRet"); - debug_loc.apply(cx.fcx); - B(cx).aggregate_ret(ret_vals); -} - -pub fn Br(cx: Block, dest: BasicBlockRef, debug_loc: DebugLoc) { - if cx.unreachable.get() { - return; - } - check_not_terminated(cx); - terminate(cx, "Br"); - debug_loc.apply(cx.fcx); - B(cx).br(dest); -} - -pub fn CondBr(cx: Block, - if_: ValueRef, - then: BasicBlockRef, - else_: BasicBlockRef, - debug_loc: DebugLoc) { - if cx.unreachable.get() { - return; - } - check_not_terminated(cx); - terminate(cx, "CondBr"); - debug_loc.apply(cx.fcx); - B(cx).cond_br(if_, then, else_); -} - -pub fn Switch(cx: Block, v: ValueRef, else_: BasicBlockRef, num_cases: usize) - -> ValueRef { - if cx.unreachable.get() { return _Undef(v); } - check_not_terminated(cx); - terminate(cx, "Switch"); - B(cx).switch(v, else_, num_cases) -} - -pub fn AddCase(s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) { - unsafe { - if llvm::LLVMIsUndef(s) == llvm::True { return; } - llvm::LLVMAddCase(s, on_val, dest); - } -} - -pub fn IndirectBr(cx: Block, - addr: ValueRef, - num_dests: usize, - debug_loc: DebugLoc) { - if cx.unreachable.get() { - return; - } - check_not_terminated(cx); - terminate(cx, "IndirectBr"); - debug_loc.apply(cx.fcx); - B(cx).indirect_br(addr, num_dests); -} - -pub fn Invoke(cx: Block, - fn_: ValueRef, - args: &[ValueRef], - then: BasicBlockRef, - catch: BasicBlockRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return C_null(Type::i8(cx.ccx())); - } - check_not_terminated(cx); - terminate(cx, "Invoke"); - debug!("Invoke({:?} with arguments ({}))", - Value(fn_), - args.iter().map(|a| { - format!("{:?}", Value(*a)) - }).collect::<Vec<String>>().join(", ")); - debug_loc.apply(cx.fcx); - let bundle = cx.lpad().and_then(|b| b.bundle()); - B(cx).invoke(fn_, args, then, catch, bundle) -} - -pub fn Unreachable(cx: Block) { - if cx.unreachable.get() { - return - } - cx.unreachable.set(true); - if !cx.terminated.get() { - B(cx).unreachable(); - } -} - -pub fn _Undef(val: ValueRef) -> ValueRef { - unsafe { - return llvm::LLVMGetUndef(val_ty(val).to_ref()); - } -} - -/* Arithmetic */ -pub fn Add(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).add(lhs, rhs) -} - -pub fn NSWAdd(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).nswadd(lhs, rhs) -} - -pub fn NUWAdd(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).nuwadd(lhs, rhs) -} - -pub fn FAdd(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).fadd(lhs, rhs) -} - -pub fn FAddFast(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).fadd_fast(lhs, rhs) -} - -pub fn Sub(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).sub(lhs, rhs) -} - -pub fn NSWSub(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).nswsub(lhs, rhs) -} - -pub fn NUWSub(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).nuwsub(lhs, rhs) -} - -pub fn FSub(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).fsub(lhs, rhs) -} - -pub fn FSubFast(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).fsub_fast(lhs, rhs) -} - -pub fn Mul(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).mul(lhs, rhs) -} - -pub fn NSWMul(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).nswmul(lhs, rhs) -} - -pub fn NUWMul(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).nuwmul(lhs, rhs) -} - -pub fn FMul(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).fmul(lhs, rhs) -} - -pub fn FMulFast(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).fmul_fast(lhs, rhs) -} - -pub fn UDiv(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).udiv(lhs, rhs) -} - -pub fn SDiv(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).sdiv(lhs, rhs) -} - -pub fn ExactSDiv(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).exactsdiv(lhs, rhs) -} - -pub fn FDiv(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).fdiv(lhs, rhs) -} - -pub fn FDivFast(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).fdiv_fast(lhs, rhs) -} - -pub fn URem(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).urem(lhs, rhs) -} - -pub fn SRem(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).srem(lhs, rhs) -} - -pub fn FRem(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).frem(lhs, rhs) -} - -pub fn FRemFast(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).frem_fast(lhs, rhs) -} - -pub fn Shl(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).shl(lhs, rhs) -} - -pub fn LShr(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).lshr(lhs, rhs) -} - -pub fn AShr(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).ashr(lhs, rhs) -} - -pub fn And(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).and(lhs, rhs) -} - -pub fn Or(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).or(lhs, rhs) -} - -pub fn Xor(cx: Block, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).xor(lhs, rhs) -} - -pub fn BinOp(cx: Block, - op: Opcode, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _Undef(lhs); - } - debug_loc.apply(cx.fcx); - B(cx).binop(op, lhs, rhs) -} - -pub fn Neg(cx: Block, v: ValueRef, debug_loc: DebugLoc) -> ValueRef { - if cx.unreachable.get() { - return _Undef(v); - } - debug_loc.apply(cx.fcx); - B(cx).neg(v) -} - -pub fn NSWNeg(cx: Block, v: ValueRef, debug_loc: DebugLoc) -> ValueRef { - if cx.unreachable.get() { - return _Undef(v); - } - debug_loc.apply(cx.fcx); - B(cx).nswneg(v) -} - -pub fn NUWNeg(cx: Block, v: ValueRef, debug_loc: DebugLoc) -> ValueRef { - if cx.unreachable.get() { - return _Undef(v); - } - debug_loc.apply(cx.fcx); - B(cx).nuwneg(v) -} -pub fn FNeg(cx: Block, v: ValueRef, debug_loc: DebugLoc) -> ValueRef { - if cx.unreachable.get() { - return _Undef(v); - } - debug_loc.apply(cx.fcx); - B(cx).fneg(v) -} - -pub fn Not(cx: Block, v: ValueRef, debug_loc: DebugLoc) -> ValueRef { - if cx.unreachable.get() { - return _Undef(v); - } - debug_loc.apply(cx.fcx); - B(cx).not(v) -} - -pub fn Alloca(cx: Block, ty: Type, name: &str) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); } - AllocaFcx(cx.fcx, ty, name) - } -} - -pub fn AllocaFcx(fcx: &FunctionContext, ty: Type, name: &str) -> ValueRef { - let b = fcx.ccx.builder(); - b.position_before(fcx.alloca_insert_pt.get().unwrap()); - DebugLoc::None.apply(fcx); - b.alloca(ty, name) -} - -pub fn Free(cx: Block, pointer_val: ValueRef) { - if cx.unreachable.get() { return; } - B(cx).free(pointer_val) -} - -pub fn Load(cx: Block, pointer_val: ValueRef) -> ValueRef { - unsafe { - let ccx = cx.fcx.ccx; - if cx.unreachable.get() { - let ty = val_ty(pointer_val); - let eltty = if ty.kind() == llvm::Array { - ty.element_type() - } else { - ccx.int_type() - }; - return llvm::LLVMGetUndef(eltty.to_ref()); - } - B(cx).load(pointer_val) - } -} - -pub fn VolatileLoad(cx: Block, pointer_val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).volatile_load(pointer_val) - } -} - -pub fn AtomicLoad(cx: Block, pointer_val: ValueRef, order: AtomicOrdering) -> ValueRef { - unsafe { - let ccx = cx.fcx.ccx; - if cx.unreachable.get() { - return llvm::LLVMGetUndef(ccx.int_type().to_ref()); - } - B(cx).atomic_load(pointer_val, order) - } -} - - -pub fn LoadRangeAssert(cx: Block, pointer_val: ValueRef, lo: u64, - hi: u64, signed: llvm::Bool) -> ValueRef { - if cx.unreachable.get() { - let ccx = cx.fcx.ccx; - let ty = val_ty(pointer_val); - let eltty = if ty.kind() == llvm::Array { - ty.element_type() - } else { - ccx.int_type() - }; - unsafe { - llvm::LLVMGetUndef(eltty.to_ref()) - } - } else { - B(cx).load_range_assert(pointer_val, lo, hi, signed) - } -} - -pub fn LoadNonNull(cx: Block, ptr: ValueRef) -> ValueRef { - if cx.unreachable.get() { - let ccx = cx.fcx.ccx; - let ty = val_ty(ptr); - let eltty = if ty.kind() == llvm::Array { - ty.element_type() - } else { - ccx.int_type() - }; - unsafe { - llvm::LLVMGetUndef(eltty.to_ref()) - } - } else { - B(cx).load_nonnull(ptr) - } -} - -pub fn Store(cx: Block, val: ValueRef, ptr: ValueRef) -> ValueRef { - if cx.unreachable.get() { return C_nil(cx.ccx()); } - B(cx).store(val, ptr) -} - -pub fn VolatileStore(cx: Block, val: ValueRef, ptr: ValueRef) -> ValueRef { - if cx.unreachable.get() { return C_nil(cx.ccx()); } - B(cx).volatile_store(val, ptr) -} - -pub fn AtomicStore(cx: Block, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) { - if cx.unreachable.get() { return; } - B(cx).atomic_store(val, ptr, order) -} - -pub fn GEP(cx: Block, pointer: ValueRef, indices: &[ValueRef]) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); - } - B(cx).gep(pointer, indices) - } -} - -// Simple wrapper around GEP that takes an array of ints and wraps them -// in C_i32() -#[inline] -pub fn GEPi(cx: Block, base: ValueRef, ixs: &[usize]) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); - } - B(cx).gepi(base, ixs) - } -} - -pub fn InBoundsGEP(cx: Block, pointer: ValueRef, indices: &[ValueRef]) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); - } - B(cx).inbounds_gep(pointer, indices) - } -} - -pub fn StructGEP(cx: Block, pointer: ValueRef, idx: usize) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).ptr_to().to_ref()); - } - B(cx).struct_gep(pointer, idx) - } -} - -pub fn GlobalString(cx: Block, _str: *const c_char) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); - } - B(cx).global_string(_str) - } -} - -pub fn GlobalStringPtr(cx: Block, _str: *const c_char) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i8p(cx.ccx()).to_ref()); - } - B(cx).global_string_ptr(_str) - } -} - -/* Casts */ -pub fn Trunc(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).trunc(val, dest_ty) - } -} - -pub fn ZExt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).zext(val, dest_ty) - } -} - -pub fn SExt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).sext(val, dest_ty) - } -} - -pub fn FPToUI(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fptoui(val, dest_ty) - } -} - -pub fn FPToSI(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fptosi(val, dest_ty) - } -} - -pub fn UIToFP(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).uitofp(val, dest_ty) - } -} - -pub fn SIToFP(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).sitofp(val, dest_ty) - } -} - -pub fn FPTrunc(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fptrunc(val, dest_ty) - } -} - -pub fn FPExt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fpext(val, dest_ty) - } -} - -pub fn PtrToInt(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).ptrtoint(val, dest_ty) - } -} - -pub fn IntToPtr(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).inttoptr(val, dest_ty) - } -} - -pub fn BitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).bitcast(val, dest_ty) - } -} - -pub fn ZExtOrBitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).zext_or_bitcast(val, dest_ty) - } -} - -pub fn SExtOrBitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).sext_or_bitcast(val, dest_ty) - } -} - -pub fn TruncOrBitCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).trunc_or_bitcast(val, dest_ty) - } -} - -pub fn Cast(cx: Block, op: Opcode, val: ValueRef, dest_ty: Type, - _: *const u8) - -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).cast(op, val, dest_ty) - } -} - -pub fn PointerCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).pointercast(val, dest_ty) - } -} - -pub fn IntCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).intcast(val, dest_ty) - } -} - -pub fn FPCast(cx: Block, val: ValueRef, dest_ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(dest_ty.to_ref()); } - B(cx).fpcast(val, dest_ty) - } -} - - -/* Comparisons */ -pub fn ICmp(cx: Block, - op: IntPredicate, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); - } - debug_loc.apply(cx.fcx); - B(cx).icmp(op, lhs, rhs) - } -} - -pub fn FCmp(cx: Block, - op: RealPredicate, - lhs: ValueRef, - rhs: ValueRef, - debug_loc: DebugLoc) - -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); - } - debug_loc.apply(cx.fcx); - B(cx).fcmp(op, lhs, rhs) - } -} - -/* Miscellaneous instructions */ -pub fn EmptyPhi(cx: Block, ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.to_ref()); } - B(cx).empty_phi(ty) - } -} - -pub fn Phi(cx: Block, ty: Type, vals: &[ValueRef], - bbs: &[BasicBlockRef]) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.to_ref()); } - B(cx).phi(ty, vals, bbs) - } -} - -pub fn AddIncomingToPhi(phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { - unsafe { - if llvm::LLVMIsUndef(phi) == llvm::True { return; } - llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); - } -} - -pub fn _UndefReturn(cx: Block, fn_: ValueRef) -> ValueRef { - unsafe { - let ccx = cx.fcx.ccx; - let ty = val_ty(fn_); - let retty = if ty.kind() == llvm::Function { - ty.return_type() - } else { - ccx.int_type() - }; - B(cx).count_insn("ret_undef"); - llvm::LLVMGetUndef(retty.to_ref()) - } -} - -pub fn add_span_comment(cx: Block, sp: Span, text: &str) { - B(cx).add_span_comment(sp, text) -} - -pub fn add_comment(cx: Block, text: &str) { - B(cx).add_comment(text) -} - -pub fn InlineAsmCall(cx: Block, asm: *const c_char, cons: *const c_char, - inputs: &[ValueRef], output: Type, - volatile: bool, alignstack: bool, - dia: AsmDialect) -> ValueRef { - B(cx).inline_asm_call(asm, cons, inputs, output, volatile, alignstack, dia) -} - -pub fn Call(cx: Block, - fn_: ValueRef, - args: &[ValueRef], - debug_loc: DebugLoc) - -> ValueRef { - if cx.unreachable.get() { - return _UndefReturn(cx, fn_); - } - debug_loc.apply(cx.fcx); - let bundle = cx.lpad.get().and_then(|b| b.bundle()); - B(cx).call(fn_, args, bundle) -} - -pub fn AtomicFence(cx: Block, order: AtomicOrdering, scope: SynchronizationScope) { - if cx.unreachable.get() { return; } - B(cx).atomic_fence(order, scope) -} - -pub fn Select(cx: Block, if_: ValueRef, then: ValueRef, else_: ValueRef) -> ValueRef { - if cx.unreachable.get() { return _Undef(then); } - B(cx).select(if_, then, else_) -} - -pub fn VAArg(cx: Block, list: ValueRef, ty: Type) -> ValueRef { - unsafe { - if cx.unreachable.get() { return llvm::LLVMGetUndef(ty.to_ref()); } - B(cx).va_arg(list, ty) - } -} - -pub fn ExtractElement(cx: Block, vec_val: ValueRef, index: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).extract_element(vec_val, index) - } -} - -pub fn InsertElement(cx: Block, vec_val: ValueRef, elt_val: ValueRef, - index: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).insert_element(vec_val, elt_val, index) - } -} - -pub fn ShuffleVector(cx: Block, v1: ValueRef, v2: ValueRef, - mask: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).shuffle_vector(v1, v2, mask) - } -} - -pub fn VectorSplat(cx: Block, num_elts: usize, elt_val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).vector_splat(num_elts, elt_val) - } -} - -pub fn ExtractValue(cx: Block, agg_val: ValueRef, index: usize) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).extract_value(agg_val, index) - } -} - -pub fn InsertValue(cx: Block, agg_val: ValueRef, elt_val: ValueRef, index: usize) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::nil(cx.ccx()).to_ref()); - } - B(cx).insert_value(agg_val, elt_val, index) - } -} - -pub fn IsNull(cx: Block, val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); - } - B(cx).is_null(val) - } -} - -pub fn IsNotNull(cx: Block, val: ValueRef) -> ValueRef { - unsafe { - if cx.unreachable.get() { - return llvm::LLVMGetUndef(Type::i1(cx.ccx()).to_ref()); - } - B(cx).is_not_null(val) - } -} - -pub fn PtrDiff(cx: Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - unsafe { - let ccx = cx.fcx.ccx; - if cx.unreachable.get() { return llvm::LLVMGetUndef(ccx.int_type().to_ref()); } - B(cx).ptrdiff(lhs, rhs) - } -} - -pub fn Trap(cx: Block) { - if cx.unreachable.get() { return; } - B(cx).trap(); -} - -pub fn LandingPad(cx: Block, ty: Type, pers_fn: ValueRef, - num_clauses: usize) -> ValueRef { - check_not_terminated(cx); - assert!(!cx.unreachable.get()); - B(cx).landing_pad(ty, pers_fn, num_clauses, cx.fcx.llfn) -} - -pub fn AddClause(cx: Block, landing_pad: ValueRef, clause: ValueRef) { - B(cx).add_clause(landing_pad, clause) -} - -pub fn SetCleanup(cx: Block, landing_pad: ValueRef) { - B(cx).set_cleanup(landing_pad) -} - -pub fn SetPersonalityFn(cx: Block, f: ValueRef) { - B(cx).set_personality_fn(f) -} - -pub fn Resume(cx: Block, exn: ValueRef) -> ValueRef { - check_not_terminated(cx); - terminate(cx, "Resume"); - B(cx).resume(exn) -} - -// Atomic Operations -pub fn AtomicCmpXchg(cx: Block, dst: ValueRef, - cmp: ValueRef, src: ValueRef, - order: AtomicOrdering, - failure_order: AtomicOrdering, - weak: llvm::Bool) -> ValueRef { - B(cx).atomic_cmpxchg(dst, cmp, src, order, failure_order, weak) -} -pub fn AtomicRMW(cx: Block, op: AtomicRmwBinOp, - dst: ValueRef, src: ValueRef, - order: AtomicOrdering) -> ValueRef { - B(cx).atomic_rmw(op, dst, src, order) -} - -pub fn CleanupPad(cx: Block, - parent: Option<ValueRef>, - args: &[ValueRef]) -> ValueRef { - check_not_terminated(cx); - assert!(!cx.unreachable.get()); - B(cx).cleanup_pad(parent, args) -} - -pub fn CleanupRet(cx: Block, - cleanup: ValueRef, - unwind: Option<BasicBlockRef>) -> ValueRef { - check_not_terminated(cx); - terminate(cx, "CleanupRet"); - B(cx).cleanup_ret(cleanup, unwind) -} - -pub fn CatchPad(cx: Block, - parent: ValueRef, - args: &[ValueRef]) -> ValueRef { - check_not_terminated(cx); - assert!(!cx.unreachable.get()); - B(cx).catch_pad(parent, args) -} - -pub fn CatchRet(cx: Block, pad: ValueRef, unwind: BasicBlockRef) -> ValueRef { - check_not_terminated(cx); - terminate(cx, "CatchRet"); - B(cx).catch_ret(pad, unwind) -} - -pub fn CatchSwitch(cx: Block, - parent: Option<ValueRef>, - unwind: Option<BasicBlockRef>, - num_handlers: usize) -> ValueRef { - check_not_terminated(cx); - terminate(cx, "CatchSwitch"); - B(cx).catch_switch(parent, unwind, num_handlers) -} - -pub fn AddHandler(cx: Block, catch_switch: ValueRef, handler: BasicBlockRef) { - B(cx).add_handler(catch_switch, handler) -} diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index 0480bb82a99..136d1aad31a 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -14,12 +14,10 @@ use llvm; use llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef}; use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; -use base; use common::*; use machine::llalign_of_pref; use type_::Type; use value::Value; -use util::nodemap::FxHashMap; use libc::{c_uint, c_char}; use std::borrow::Cow; @@ -32,65 +30,40 @@ pub struct Builder<'a, 'tcx: 'a> { pub ccx: &'a CrateContext<'a, 'tcx>, } +impl<'a, 'tcx> Drop for Builder<'a, 'tcx> { + fn drop(&mut self) { + unsafe { + llvm::LLVMDisposeBuilder(self.llbuilder); + } + } +} + // This is a really awful way to get a zero-length c-string, but better (and a // lot more efficient) than doing str::as_c_str("", ...) every time. -pub fn noname() -> *const c_char { +fn noname() -> *const c_char { static CNULL: c_char = 0; &CNULL } impl<'a, 'tcx> Builder<'a, 'tcx> { - pub fn new(ccx: &'a CrateContext<'a, 'tcx>) -> Builder<'a, 'tcx> { + pub fn with_ccx(ccx: &'a CrateContext<'a, 'tcx>) -> Self { + // Create a fresh builder from the crate context. + let llbuilder = unsafe { + llvm::LLVMCreateBuilderInContext(ccx.llcx()) + }; Builder { - llbuilder: ccx.raw_builder(), + llbuilder: llbuilder, ccx: ccx, } } - pub fn count_insn(&self, category: &str) { + fn count_insn(&self, category: &str) { if self.ccx.sess().trans_stats() { - self.ccx.stats().n_llvm_insns.set(self.ccx - .stats() - .n_llvm_insns - .get() + 1); + self.ccx.stats().n_llvm_insns.set(self.ccx.stats().n_llvm_insns.get() + 1); } - self.ccx.count_llvm_insn(); if self.ccx.sess().count_llvm_insns() { - base::with_insn_ctxt(|v| { - let mut h = self.ccx.stats().llvm_insns.borrow_mut(); - - // Build version of path with cycles removed. - - // Pass 1: scan table mapping str -> rightmost pos. - let mut mm = FxHashMap(); - let len = v.len(); - let mut i = 0; - while i < len { - mm.insert(v[i], i); - i += 1; - } - - // Pass 2: concat strings for each elt, skipping - // forwards over any cycles by advancing to rightmost - // occurrence of each element in path. - let mut s = String::from("."); - i = 0; - while i < len { - i = mm[v[i]]; - s.push('/'); - s.push_str(v[i]); - i += 1; - } - - s.push('/'); - s.push_str(category); - - let n = match h.get(&s) { - Some(&n) => n, - _ => 0 - }; - h.insert(s, n+1); - }) + let mut h = self.ccx.stats().llvm_insns.borrow_mut(); + *h.entry(category.to_string()).or_insert(0) += 1; } } @@ -462,7 +435,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn alloca(&self, ty: Type, name: &str) -> ValueRef { + pub fn dynamic_alloca(&self, ty: Type, name: &str) -> ValueRef { self.count_insn("alloca"); unsafe { if name.is_empty() { @@ -1103,6 +1076,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + pub fn add_case(&self, s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) { + unsafe { + if llvm::LLVMIsUndef(s) == llvm::True { return; } + llvm::LLVMAddCase(s, on_val, dest) + } + } + + pub fn add_incoming_to_phi(&self, phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { + unsafe { + if llvm::LLVMIsUndef(phi) == llvm::True { return; } + llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); + } + } + /// Returns the ptr value that should be used for storing `val`. fn check_store<'b>(&self, val: ValueRef, diff --git a/src/librustc_trans/cabi_arm.rs b/src/librustc_trans/cabi_arm.rs index 93d43f7d961..85b26074bae 100644 --- a/src/librustc_trans/cabi_arm.rs +++ b/src/librustc_trans/cabi_arm.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(non_upper_case_globals)] - use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; use abi::{self, align_up_to, FnType, ArgType}; use context::CrateContext; diff --git a/src/librustc_trans/cabi_sparc.rs b/src/librustc_trans/cabi_sparc.rs new file mode 100644 index 00000000000..25fe53e7ef4 --- /dev/null +++ b/src/librustc_trans/cabi_sparc.rs @@ -0,0 +1,108 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use libc::c_uint; +use std::cmp; +use llvm; +use llvm::{Integer, Pointer, Float, Double, Vector}; +use abi::{self, align_up_to, ArgType, FnType}; +use context::CrateContext; +use type_::Type; + +fn ty_align(ty: Type) -> usize { + abi::ty_align(ty, 4) +} + +fn ty_size(ty: Type) -> usize { + abi::ty_size(ty, 4) +} + +fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) { + if is_reg_ty(ret.ty) { + ret.extend_integer_width_to(32); + } else { + ret.make_indirect(ccx); + } +} + +fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType, offset: &mut usize) { + let orig_offset = *offset; + let size = ty_size(arg.ty) * 8; + let mut align = ty_align(arg.ty); + + align = cmp::min(cmp::max(align, 4), 8); + *offset = align_up_to(*offset, align); + *offset += align_up_to(size, align * 8) / 8; + + if !is_reg_ty(arg.ty) { + arg.cast = Some(struct_ty(ccx, arg.ty)); + arg.pad = padding_ty(ccx, align, orig_offset); + } else { + arg.extend_integer_width_to(32); + } +} + +fn is_reg_ty(ty: Type) -> bool { + return match ty.kind() { + Integer + | Pointer + | Float + | Double + | Vector => true, + _ => false + }; +} + +fn padding_ty(ccx: &CrateContext, align: usize, offset: usize) -> Option<Type> { + if ((align - 1 ) & offset) > 0 { + Some(Type::i32(ccx)) + } else { + None + } +} + +fn coerce_to_int(ccx: &CrateContext, size: usize) -> Vec<Type> { + let int_ty = Type::i32(ccx); + let mut args = Vec::new(); + + let mut n = size / 32; + while n > 0 { + args.push(int_ty); + n -= 1; + } + + let r = size % 32; + if r > 0 { + unsafe { + args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint))); + } + } + + args +} + +fn struct_ty(ccx: &CrateContext, ty: Type) -> Type { + let size = ty_size(ty) * 8; + Type::struct_(ccx, &coerce_to_int(ccx, size), false) +} + +pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) { + if !fty.ret.is_ignore() { + classify_ret_ty(ccx, &mut fty.ret); + } + + let mut offset = if fty.ret.is_indirect() { 4 } else { 0 }; + for arg in &mut fty.args { + if arg.is_ignore() { continue; } + classify_arg_ty(ccx, arg, &mut offset); + } +} diff --git a/src/librustc_trans/cabi_x86.rs b/src/librustc_trans/cabi_x86.rs index ce85234f203..fea005f3d77 100644 --- a/src/librustc_trans/cabi_x86.rs +++ b/src/librustc_trans/cabi_x86.rs @@ -14,7 +14,13 @@ use type_::Type; use super::common::*; use super::machine::*; -pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) { +#[derive(PartialEq)] +pub enum Flavor { + General, + Fastcall +} + +pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType, flavor: Flavor) { if !fty.ret.is_ignore() { if fty.ret.ty.kind() == Struct { // Returning a structure. Most often, this will use @@ -51,4 +57,46 @@ pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) { arg.extend_integer_width_to(32); } } + + if flavor == Flavor::Fastcall { + // Mark arguments as InReg like clang does it, + // so our fastcall is compatible with C/C++ fastcall. + + // Clang reference: lib/CodeGen/TargetInfo.cpp + // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs() + + // IsSoftFloatABI is only set to true on ARM platforms, + // which in turn can't be x86? + + let mut free_regs = 2; + + for arg in &mut fty.args { + if arg.is_ignore() || arg.is_indirect() { continue; } + + if arg.ty.kind() == Float { + continue; + } + + let size = llbitsize_of_real(ccx, arg.ty); + let size_in_regs = (size + 31) / 32; + + if size_in_regs == 0 { + continue; + } + + if size_in_regs > free_regs { + break; + } + + free_regs -= size_in_regs; + + if size <= 32 && (arg.ty.kind() == Pointer || arg.ty.kind() == Integer) { + arg.attrs.set(ArgAttribute::InReg); + } + + if free_regs == 0 { + break; + } + } + } } diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index d7e9f1372e0..1abe25ea607 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -16,7 +16,6 @@ pub use self::CalleeData::*; -use arena::TypedArena; use llvm::{self, ValueRef, get_params}; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; @@ -25,10 +24,11 @@ use abi::{Abi, FnType}; use attributes; use base; use base::*; -use build::*; -use common::{self, Block, Result, CrateContext, FunctionContext, SharedCrateContext}; +use common::{ + self, CrateContext, FunctionContext, SharedCrateContext +}; +use adt::MaybeSizedValue; use consts; -use debuginfo::DebugLoc; use declare; use value::Value; use meth; @@ -71,25 +71,8 @@ impl<'tcx> Callee<'tcx> { } } - /// Trait or impl method call. - pub fn method_call<'blk>(bcx: Block<'blk, 'tcx>, - method_call: ty::MethodCall) - -> Callee<'tcx> { - let method = bcx.tcx().tables().method_map[&method_call]; - Callee::method(bcx, method) - } - - /// Trait or impl method. - pub fn method<'blk>(bcx: Block<'blk, 'tcx>, - method: ty::MethodCallee<'tcx>) -> Callee<'tcx> { - let substs = bcx.fcx.monomorphize(&method.substs); - Callee::def(bcx.ccx(), method.def_id, substs) - } - /// Function or method definition. - pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>) + pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Callee<'tcx> { let tcx = ccx.tcx(); @@ -196,25 +179,6 @@ impl<'tcx> Callee<'tcx> { fn_ty } - /// This behemoth of a function translates function calls. Unfortunately, in - /// order to generate more efficient LLVM output at -O0, it has quite a complex - /// signature (refactoring this into two functions seems like a good idea). - /// - /// In particular, for lang items, it is invoked with a dest of None, and in - /// that case the return value contains the result of the fn. The lang item must - /// not return a structural type or else all heck breaks loose. - /// - /// For non-lang items, `dest` is always Some, and hence the result is written - /// into memory somewhere. Nonetheless we return the actual return value of the - /// function. - pub fn call<'a, 'blk>(self, bcx: Block<'blk, 'tcx>, - debug_loc: DebugLoc, - args: &[ValueRef], - dest: Option<ValueRef>) - -> Result<'blk, 'tcx> { - trans_call_inner(bcx, debug_loc, self, args, dest) - } - /// Turn the callee into a function pointer. pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef { match self.data { @@ -267,8 +231,6 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, // then adapt the self type let llfn_closure_kind = ccx.tcx().closure_kind(def_id); - let _icx = push_ctxt("trans_closure_adapter_shim"); - debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \ trait_closure_kind={:?}, llfn={:?})", llfn_closure_kind, trait_closure_kind, Value(llfn)); @@ -367,23 +329,28 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty); attributes::set_frame_pointer_elimination(ccx, lloncefn); - let (block_arena, fcx): (TypedArena<_>, FunctionContext); - block_arena = TypedArena::new(); - fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena); - let mut bcx = fcx.init(false); + let orig_fn_ty = fn_ty; + let fcx = FunctionContext::new(ccx, lloncefn); + let mut bcx = fcx.get_entry_block(); + let callee = Callee { + data: Fn(llreffn), + ty: llref_fn_ty + }; // the first argument (`self`) will be the (by value) closure env. let mut llargs = get_params(fcx.llfn); - let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize; - let env_arg = &fcx.fn_ty.args[0]; + let fn_ret = callee.ty.fn_ret(); + let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); + let self_idx = fn_ty.ret.is_indirect() as usize; + let env_arg = &orig_fn_ty.args[0]; let llenv = if env_arg.is_indirect() { llargs[self_idx] } else { - let scratch = alloc_ty(bcx, closure_ty, "self"); + let scratch = alloc_ty(&bcx, closure_ty, "self"); let mut llarg_idx = self_idx; - env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, scratch); + env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch); scratch }; @@ -391,33 +358,37 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // Adjust llargs such that llargs[self_idx..] has the call arguments. // For zero-sized closures that means sneaking in a new argument. if env_arg.is_ignore() { - if self_idx > 0 { - self_idx -= 1; - llargs[self_idx] = llenv; - } else { - llargs.insert(0, llenv); - } + llargs.insert(self_idx, llenv); } else { llargs[self_idx] = llenv; } - let dest = fcx.llretslotptr.get(); - - let callee = Callee { - data: Fn(llreffn), - ty: llref_fn_ty - }; - // Call the by-ref closure body with `self` in a cleanup scope, // to drop `self` when the body returns, or in case it unwinds. - let self_scope = fcx.push_custom_cleanup_scope(); - fcx.schedule_drop_mem(self_scope, llenv, closure_ty); - - bcx = callee.call(bcx, DebugLoc::None, &llargs[self_idx..], dest).bcx; + let self_scope = fcx.schedule_drop_mem(MaybeSizedValue::sized(llenv), closure_ty); + + let llfn = callee.reify(bcx.ccx); + let llret; + if let Some(landing_pad) = self_scope.landing_pad { + let normal_bcx = bcx.fcx().build_new_block("normal-return"); + llret = bcx.invoke(llfn, &llargs[..], normal_bcx.llbb(), landing_pad, None); + bcx = normal_bcx; + } else { + llret = bcx.call(llfn, &llargs[..], None); + } + fn_ty.apply_attrs_callsite(llret); - fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope); + if fn_ret.0.is_never() { + bcx.unreachable(); + } else { + self_scope.trans(&bcx); - fcx.finish(bcx, DebugLoc::None); + if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() { + bcx.ret_void(); + } else { + bcx.ret(llret); + } + } ccx.instances().borrow_mut().insert(method_instance, lloncefn); @@ -443,7 +414,6 @@ fn trans_fn_pointer_shim<'a, 'tcx>( bare_fn_ty: Ty<'tcx>) -> ValueRef { - let _icx = push_ctxt("trans_fn_pointer_shim"); let tcx = ccx.tcx(); // Normalize the type for better caching. @@ -519,32 +489,39 @@ fn trans_fn_pointer_shim<'a, 'tcx>( let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty); attributes::set_frame_pointer_elimination(ccx, llfn); // - let (block_arena, fcx): (TypedArena<_>, FunctionContext); - block_arena = TypedArena::new(); - fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &block_arena); - let mut bcx = fcx.init(false); + let fcx = FunctionContext::new(ccx, llfn); + let bcx = fcx.get_entry_block(); - let llargs = get_params(fcx.llfn); + let mut llargs = get_params(fcx.llfn); - let self_idx = fcx.fn_ty.ret.is_indirect() as usize; + let self_arg = llargs.remove(fn_ty.ret.is_indirect() as usize); let llfnpointer = llfnpointer.unwrap_or_else(|| { // the first argument (`self`) will be ptr to the fn pointer if is_by_ref { - Load(bcx, llargs[self_idx]) + bcx.load(self_arg) } else { - llargs[self_idx] + self_arg } }); - let dest = fcx.llretslotptr.get(); - let callee = Callee { data: Fn(llfnpointer), ty: bare_fn_ty }; - bcx = callee.call(bcx, DebugLoc::None, &llargs[(self_idx + 1)..], dest).bcx; + let fn_ret = callee.ty.fn_ret(); + let fn_ty = callee.direct_fn_type(ccx, &[]); + let llret = bcx.call(llfnpointer, &llargs, None); + fn_ty.apply_attrs_callsite(llret); - fcx.finish(bcx, DebugLoc::None); + if fn_ret.0.is_never() { + bcx.unreachable(); + } else { + if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() { + bcx.ret_void(); + } else { + bcx.ret(llret); + } + } ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn); @@ -649,87 +626,3 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, (llfn, fn_ty) } - -// ______________________________________________________________________ -// Translating calls - -fn trans_call_inner<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - debug_loc: DebugLoc, - callee: Callee<'tcx>, - args: &[ValueRef], - opt_llretslot: Option<ValueRef>) - -> Result<'blk, 'tcx> { - // Introduce a temporary cleanup scope that will contain cleanups - // for the arguments while they are being evaluated. The purpose - // this cleanup is to ensure that, should a panic occur while - // evaluating argument N, the values for arguments 0...N-1 are all - // cleaned up. If no panic occurs, the values are handed off to - // the callee, and hence none of the cleanups in this temporary - // scope will ever execute. - let fcx = bcx.fcx; - let ccx = fcx.ccx; - - let fn_ret = callee.ty.fn_ret(); - let fn_ty = callee.direct_fn_type(ccx, &[]); - - let mut callee = match callee.data { - NamedTupleConstructor(_) | Intrinsic => { - bug!("{:?} calls should not go through Callee::call", callee); - } - f => f - }; - - // If there no destination, return must be direct, with no cast. - if opt_llretslot.is_none() { - assert!(!fn_ty.ret.is_indirect() && fn_ty.ret.cast.is_none()); - } - - let mut llargs = Vec::new(); - - if fn_ty.ret.is_indirect() { - let mut llretslot = opt_llretslot.unwrap(); - if let Some(ty) = fn_ty.ret.cast { - llretslot = PointerCast(bcx, llretslot, ty.ptr_to()); - } - llargs.push(llretslot); - } - - match callee { - Virtual(idx) => { - llargs.push(args[0]); - - let fn_ptr = meth::get_virtual_method(bcx, args[1], idx); - let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to(); - callee = Fn(PointerCast(bcx, fn_ptr, llty)); - llargs.extend_from_slice(&args[2..]); - } - _ => llargs.extend_from_slice(args) - } - - let llfn = match callee { - Fn(f) => f, - _ => bug!("expected fn pointer callee, found {:?}", callee) - }; - - let (llret, bcx) = base::invoke(bcx, llfn, &llargs, debug_loc); - if !bcx.unreachable.get() { - fn_ty.apply_attrs_callsite(llret); - - // If the function we just called does not use an outpointer, - // store the result into the rust outpointer. Cast the outpointer - // type to match because some ABIs will use a different type than - // the Rust type. e.g., a {u32,u32} struct could be returned as - // u64. - if !fn_ty.ret.is_indirect() { - if let Some(llretslot) = opt_llretslot { - fn_ty.ret.store(&bcx.build(), llret, llretslot); - } - } - } - - if fn_ret.0.is_never() { - Unreachable(bcx); - } - - Result::new(bcx, llret) -} diff --git a/src/librustc_trans/cleanup.rs b/src/librustc_trans/cleanup.rs index b9f24eba9dc..4e59ea3f6c5 100644 --- a/src/librustc_trans/cleanup.rs +++ b/src/librustc_trans/cleanup.rs @@ -11,216 +11,98 @@ //! ## The Cleanup module //! //! The cleanup module tracks what values need to be cleaned up as scopes -//! are exited, either via panic or just normal control flow. The basic -//! idea is that the function context maintains a stack of cleanup scopes -//! that are pushed/popped as we traverse the AST tree. There is typically -//! at least one cleanup scope per AST node; some AST nodes may introduce -//! additional temporary scopes. +//! are exited, either via panic or just normal control flow. //! //! Cleanup items can be scheduled into any of the scopes on the stack. -//! Typically, when a scope is popped, we will also generate the code for -//! each of its cleanups at that time. This corresponds to a normal exit -//! from a block (for example, an expression completing evaluation -//! successfully without panic). However, it is also possible to pop a -//! block *without* executing its cleanups; this is typically used to -//! guard intermediate values that must be cleaned up on panic, but not -//! if everything goes right. See the section on custom scopes below for -//! more details. -//! -//! Cleanup scopes come in three kinds: -//! -//! - **AST scopes:** each AST node in a function body has a corresponding -//! AST scope. We push the AST scope when we start generate code for an AST -//! node and pop it once the AST node has been fully generated. -//! - **Loop scopes:** loops have an additional cleanup scope. Cleanups are -//! never scheduled into loop scopes; instead, they are used to record the -//! basic blocks that we should branch to when a `continue` or `break` statement -//! is encountered. -//! - **Custom scopes:** custom scopes are typically used to ensure cleanup -//! of intermediate values. -//! -//! ### When to schedule cleanup -//! -//! Although the cleanup system is intended to *feel* fairly declarative, -//! it's still important to time calls to `schedule_clean()` correctly. -//! Basically, you should not schedule cleanup for memory until it has -//! been initialized, because if an unwind should occur before the memory -//! is fully initialized, then the cleanup will run and try to free or -//! drop uninitialized memory. If the initialization itself produces -//! byproducts that need to be freed, then you should use temporary custom -//! scopes to ensure that those byproducts will get freed on unwind. For -//! example, an expression like `box foo()` will first allocate a box in the -//! heap and then call `foo()` -- if `foo()` should panic, this box needs -//! to be *shallowly* freed. -//! -//! ### Long-distance jumps -//! -//! In addition to popping a scope, which corresponds to normal control -//! flow exiting the scope, we may also *jump out* of a scope into some -//! earlier scope on the stack. This can occur in response to a `return`, -//! `break`, or `continue` statement, but also in response to panic. In -//! any of these cases, we will generate a series of cleanup blocks for -//! each of the scopes that is exited. So, if the stack contains scopes A -//! ... Z, and we break out of a loop whose corresponding cleanup scope is -//! X, we would generate cleanup blocks for the cleanups in X, Y, and Z. -//! After cleanup is done we would branch to the exit point for scope X. -//! But if panic should occur, we would generate cleanups for all the -//! scopes from A to Z and then resume the unwind process afterwards. -//! -//! To avoid generating tons of code, we cache the cleanup blocks that we -//! create for breaks, returns, unwinds, and other jumps. Whenever a new -//! cleanup is scheduled, though, we must clear these cached blocks. A -//! possible improvement would be to keep the cached blocks but simply -//! generate a new block which performs the additional cleanup and then -//! branches to the existing cached blocks. -//! -//! ### AST and loop cleanup scopes -//! -//! AST cleanup scopes are pushed when we begin and end processing an AST -//! node. They are used to house cleanups related to rvalue temporary that -//! get referenced (e.g., due to an expression like `&Foo()`). Whenever an -//! AST scope is popped, we always trans all the cleanups, adding the cleanup -//! code after the postdominator of the AST node. -//! -//! AST nodes that represent breakable loops also push a loop scope; the -//! loop scope never has any actual cleanups, it's just used to point to -//! the basic blocks where control should flow after a "continue" or -//! "break" statement. Popping a loop scope never generates code. -//! -//! ### Custom cleanup scopes -//! -//! Custom cleanup scopes are used for a variety of purposes. The most -//! common though is to handle temporary byproducts, where cleanup only -//! needs to occur on panic. The general strategy is to push a custom -//! cleanup scope, schedule *shallow* cleanups into the custom scope, and -//! then pop the custom scope (without transing the cleanups) when -//! execution succeeds normally. This way the cleanups are only trans'd on -//! unwind, and only up until the point where execution succeeded, at -//! which time the complete value should be stored in an lvalue or some -//! other place where normal cleanup applies. -//! -//! To spell it out, here is an example. Imagine an expression `box expr`. -//! We would basically: -//! -//! 1. Push a custom cleanup scope C. -//! 2. Allocate the box. -//! 3. Schedule a shallow free in the scope C. -//! 4. Trans `expr` into the box. -//! 5. Pop the scope C. -//! 6. Return the box as an rvalue. -//! -//! This way, if a panic occurs while transing `expr`, the custom -//! cleanup scope C is pushed and hence the box will be freed. The trans -//! code for `expr` itself is responsible for freeing any other byproducts -//! that may be in play. - -pub use self::EarlyExitLabel::*; +//! Typically, when a scope is finished, we generate the cleanup code. This +//! corresponds to a normal exit from a block (for example, an expression +//! completing evaluation successfully without panic). -use llvm::{BasicBlockRef, ValueRef}; +use llvm::BasicBlockRef; use base; -use build; -use common; -use common::{Block, FunctionContext, LandingPad}; -use debuginfo::{DebugLoc}; +use adt::MaybeSizedValue; +use common::{BlockAndBuilder, FunctionContext, Funclet}; use glue; use type_::Type; -use value::Value; use rustc::ty::Ty; pub struct CleanupScope<'tcx> { - // Cleanups to run upon scope exit. - cleanups: Vec<DropValue<'tcx>>, - - // The debug location any drop calls generated for this scope will be - // associated with. - debug_loc: DebugLoc, + // Cleanup to run upon scope exit. + cleanup: Option<DropValue<'tcx>>, - cached_early_exits: Vec<CachedEarlyExit>, - cached_landing_pad: Option<BasicBlockRef>, + // Computed on creation if compiling with landing pads (!sess.no_landing_pads) + pub landing_pad: Option<BasicBlockRef>, } -#[derive(Copy, Clone, Debug)] -pub struct CustomScopeIndex { - index: usize +#[derive(Copy, Clone)] +pub struct DropValue<'tcx> { + val: MaybeSizedValue, + ty: Ty<'tcx>, + skip_dtor: bool, } -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum EarlyExitLabel { - UnwindExit(UnwindKind), -} +impl<'tcx> DropValue<'tcx> { + fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &BlockAndBuilder<'a, 'tcx>) { + glue::call_drop_glue(bcx, self.val, self.ty, self.skip_dtor, funclet) + } -#[derive(Copy, Clone, Debug)] -pub enum UnwindKind { - LandingPad, - CleanupPad(ValueRef), -} + /// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary + /// for an unwind and then `resume` to continue error propagation: + /// + /// landing_pad -> ... cleanups ... -> [resume] + /// + /// This should only be called once per function, as it creates an alloca for the landingpad. + fn get_landing_pad<'a>(&self, fcx: &FunctionContext<'a, 'tcx>) -> BasicBlockRef { + debug!("get_landing_pad"); + let bcx = fcx.build_new_block("cleanup_unwind"); + let llpersonality = bcx.ccx.eh_personality(); + bcx.set_personality_fn(llpersonality); -#[derive(Copy, Clone)] -pub struct CachedEarlyExit { - label: EarlyExitLabel, - cleanup_block: BasicBlockRef, - last_cleanup: usize, -} + if base::wants_msvc_seh(fcx.ccx.sess()) { + let pad = bcx.cleanup_pad(None, &[]); + let funclet = Some(Funclet::new(pad)); + self.trans(funclet.as_ref(), &bcx); -impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> { - pub fn push_custom_cleanup_scope(&self) -> CustomScopeIndex { - let index = self.scopes_len(); - debug!("push_custom_cleanup_scope(): {}", index); + bcx.cleanup_ret(pad, None); + } else { + // The landing pad return type (the type being propagated). Not sure + // what this represents but it's determined by the personality + // function and this is what the EH proposal example uses. + let llretty = Type::struct_(fcx.ccx, &[Type::i8p(fcx.ccx), Type::i32(fcx.ccx)], false); - // Just copy the debuginfo source location from the enclosing scope - let debug_loc = self.scopes - .borrow() - .last() - .map(|opt_scope| opt_scope.debug_loc) - .unwrap_or(DebugLoc::None); + // The only landing pad clause will be 'cleanup' + let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.fcx().llfn); - self.push_scope(CleanupScope::new(debug_loc)); - CustomScopeIndex { index: index } - } + // The landing pad block is a cleanup + bcx.set_cleanup(llretval); - /// Removes the top cleanup scope from the stack without executing its cleanups. The top - /// cleanup scope must be the temporary scope `custom_scope`. - pub fn pop_custom_cleanup_scope(&self, - custom_scope: CustomScopeIndex) { - debug!("pop_custom_cleanup_scope({})", custom_scope.index); - assert!(self.is_valid_to_pop_custom_scope(custom_scope)); - let _ = self.pop_scope(); - } + // Insert cleanup instructions into the cleanup block + self.trans(None, &bcx); - /// Removes the top cleanup scope from the stack, which must be a temporary scope, and - /// generates the code to do its cleanups for normal exit. - pub fn pop_and_trans_custom_cleanup_scope(&self, - bcx: Block<'blk, 'tcx>, - custom_scope: CustomScopeIndex) - -> Block<'blk, 'tcx> { - debug!("pop_and_trans_custom_cleanup_scope({:?})", custom_scope); - assert!(self.is_valid_to_pop_custom_scope(custom_scope)); + if !bcx.sess().target.target.options.custom_unwind_resume { + bcx.resume(llretval); + } else { + let exc_ptr = bcx.extract_value(llretval, 0); + bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], None); + bcx.unreachable(); + } + } - let scope = self.pop_scope(); - self.trans_scope_cleanups(bcx, &scope) + bcx.llbb() } +} - /// Schedules a (deep) drop of `val`, which is a pointer to an instance of - /// `ty` - pub fn schedule_drop_mem(&self, - cleanup_scope: CustomScopeIndex, - val: ValueRef, - ty: Ty<'tcx>) { - if !self.type_needs_drop(ty) { return; } +impl<'a, 'tcx> FunctionContext<'a, 'tcx> { + /// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty` + pub fn schedule_drop_mem(&self, val: MaybeSizedValue, ty: Ty<'tcx>) -> CleanupScope<'tcx> { + if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); } let drop = DropValue { - is_immediate: false, val: val, ty: ty, skip_dtor: false, }; - debug!("schedule_drop_mem({:?}, val={:?}, ty={:?}) skip_dtor={}", - cleanup_scope, - Value(val), - ty, - drop.skip_dtor); - - self.schedule_clean(cleanup_scope, drop); + CleanupScope::new(self, drop) } /// Issue #23611: Schedules a (deep) drop of the contents of @@ -228,477 +110,44 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> { /// `ty`. The scheduled code handles extracting the discriminant /// and dropping the contents associated with that variant /// *without* executing any associated drop implementation. - pub fn schedule_drop_adt_contents(&self, - cleanup_scope: CustomScopeIndex, - val: ValueRef, - ty: Ty<'tcx>) { + pub fn schedule_drop_adt_contents(&self, val: MaybeSizedValue, ty: Ty<'tcx>) + -> CleanupScope<'tcx> { // `if` below could be "!contents_needs_drop"; skipping drop // is just an optimization, so sound to be conservative. - if !self.type_needs_drop(ty) { return; } + if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); } let drop = DropValue { - is_immediate: false, val: val, ty: ty, skip_dtor: true, }; - debug!("schedule_drop_adt_contents({:?}, val={:?}, ty={:?}) skip_dtor={}", - cleanup_scope, - Value(val), - ty, - drop.skip_dtor); - - self.schedule_clean(cleanup_scope, drop); - } - - /// Schedules a (deep) drop of `val`, which is an instance of `ty` - pub fn schedule_drop_immediate(&self, - cleanup_scope: CustomScopeIndex, - val: ValueRef, - ty: Ty<'tcx>) { - - if !self.type_needs_drop(ty) { return; } - let drop = DropValue { - is_immediate: true, - val: val, - ty: ty, - skip_dtor: false, - }; - - debug!("schedule_drop_immediate({:?}, val={:?}, ty={:?}) skip_dtor={}", - cleanup_scope, - Value(val), - ty, - drop.skip_dtor); - - self.schedule_clean(cleanup_scope, drop); - } - - /// Schedules a cleanup to occur in the top-most scope, which must be a temporary scope. - fn schedule_clean(&self, custom_scope: CustomScopeIndex, cleanup: DropValue<'tcx>) { - debug!("schedule_clean_in_custom_scope(custom_scope={})", - custom_scope.index); - - assert!(self.is_valid_custom_scope(custom_scope)); - - let mut scopes = self.scopes.borrow_mut(); - let scope = &mut (*scopes)[custom_scope.index]; - scope.cleanups.push(cleanup); - scope.cached_landing_pad = None; - } - - /// Returns true if there are pending cleanups that should execute on panic. - pub fn needs_invoke(&self) -> bool { - self.scopes.borrow().iter().rev().any(|s| s.needs_invoke()) - } - - /// Returns a basic block to branch to in the event of a panic. This block - /// will run the panic cleanups and eventually resume the exception that - /// caused the landing pad to be run. - pub fn get_landing_pad(&'blk self) -> BasicBlockRef { - let _icx = base::push_ctxt("get_landing_pad"); - - debug!("get_landing_pad"); - - let orig_scopes_len = self.scopes_len(); - assert!(orig_scopes_len > 0); - - // Remove any scopes that do not have cleanups on panic: - let mut popped_scopes = vec![]; - while !self.top_scope(|s| s.needs_invoke()) { - debug!("top scope does not need invoke"); - popped_scopes.push(self.pop_scope()); - } - - // Check for an existing landing pad in the new topmost scope: - let llbb = self.get_or_create_landing_pad(); - - // Push the scopes we removed back on: - loop { - match popped_scopes.pop() { - Some(scope) => self.push_scope(scope), - None => break - } - } - - assert_eq!(self.scopes_len(), orig_scopes_len); - - return llbb; - } - - fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool { - self.is_valid_custom_scope(custom_scope) && - custom_scope.index == self.scopes.borrow().len() - 1 - } - - fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool { - let scopes = self.scopes.borrow(); - custom_scope.index < scopes.len() - } - - /// Generates the cleanups for `scope` into `bcx` - fn trans_scope_cleanups(&self, // cannot borrow self, will recurse - bcx: Block<'blk, 'tcx>, - scope: &CleanupScope<'tcx>) -> Block<'blk, 'tcx> { - - let mut bcx = bcx; - if !bcx.unreachable.get() { - for cleanup in scope.cleanups.iter().rev() { - bcx = cleanup.trans(bcx, scope.debug_loc); - } - } - bcx - } - - fn scopes_len(&self) -> usize { - self.scopes.borrow().len() - } - - fn push_scope(&self, scope: CleanupScope<'tcx>) { - self.scopes.borrow_mut().push(scope) - } - - fn pop_scope(&self) -> CleanupScope<'tcx> { - debug!("popping cleanup scope {}, {} scopes remaining", - self.top_scope(|s| s.block_name("")), - self.scopes_len() - 1); - - self.scopes.borrow_mut().pop().unwrap() - } - - fn top_scope<R, F>(&self, f: F) -> R where F: FnOnce(&CleanupScope<'tcx>) -> R { - f(self.scopes.borrow().last().unwrap()) - } - - /// Used when the caller wishes to jump to an early exit, such as a return, - /// break, continue, or unwind. This function will generate all cleanups - /// between the top of the stack and the exit `label` and return a basic - /// block that the caller can branch to. - /// - /// For example, if the current stack of cleanups were as follows: - /// - /// AST 22 - /// Custom 1 - /// AST 23 - /// Loop 23 - /// Custom 2 - /// AST 24 - /// - /// and the `label` specifies a break from `Loop 23`, then this function - /// would generate a series of basic blocks as follows: - /// - /// Cleanup(AST 24) -> Cleanup(Custom 2) -> break_blk - /// - /// where `break_blk` is the block specified in `Loop 23` as the target for - /// breaks. The return value would be the first basic block in that sequence - /// (`Cleanup(AST 24)`). The caller could then branch to `Cleanup(AST 24)` - /// and it will perform all cleanups and finally branch to the `break_blk`. - fn trans_cleanups_to_exit_scope(&'blk self, - label: EarlyExitLabel) - -> BasicBlockRef { - debug!("trans_cleanups_to_exit_scope label={:?} scopes={}", - label, self.scopes_len()); - - let orig_scopes_len = self.scopes_len(); - let mut prev_llbb; - let mut popped_scopes = vec![]; - let mut skip = 0; - - // First we pop off all the cleanup stacks that are - // traversed until the exit is reached, pushing them - // onto the side vector `popped_scopes`. No code is - // generated at this time. - // - // So, continuing the example from above, we would wind up - // with a `popped_scopes` vector of `[AST 24, Custom 2]`. - // (Presuming that there are no cached exits) - loop { - if self.scopes_len() == 0 { - match label { - UnwindExit(val) => { - // Generate a block that will resume unwinding to the - // calling function - let bcx = self.new_block("resume"); - match val { - UnwindKind::LandingPad => { - let addr = self.landingpad_alloca.get() - .unwrap(); - let lp = build::Load(bcx, addr); - base::call_lifetime_end(bcx, addr); - base::trans_unwind_resume(bcx, lp); - } - UnwindKind::CleanupPad(_) => { - let pad = build::CleanupPad(bcx, None, &[]); - build::CleanupRet(bcx, pad, None); - } - } - prev_llbb = bcx.llbb; - break; - } - } - } - - // Pop off the scope, since we may be generating - // unwinding code for it. - let top_scope = self.pop_scope(); - let cached_exit = top_scope.cached_early_exit(label); - popped_scopes.push(top_scope); - - // Check if we have already cached the unwinding of this - // scope for this label. If so, we can stop popping scopes - // and branch to the cached label, since it contains the - // cleanups for any subsequent scopes. - if let Some((exit, last_cleanup)) = cached_exit { - prev_llbb = exit; - skip = last_cleanup; - break; - } - } - - debug!("trans_cleanups_to_exit_scope: popped {} scopes", - popped_scopes.len()); - - // Now push the popped scopes back on. As we go, - // we track in `prev_llbb` the exit to which this scope - // should branch when it's done. - // - // So, continuing with our example, we will start out with - // `prev_llbb` being set to `break_blk` (or possibly a cached - // early exit). We will then pop the scopes from `popped_scopes` - // and generate a basic block for each one, prepending it in the - // series and updating `prev_llbb`. So we begin by popping `Custom 2` - // and generating `Cleanup(Custom 2)`. We make `Cleanup(Custom 2)` - // branch to `prev_llbb == break_blk`, giving us a sequence like: - // - // Cleanup(Custom 2) -> prev_llbb - // - // We then pop `AST 24` and repeat the process, giving us the sequence: - // - // Cleanup(AST 24) -> Cleanup(Custom 2) -> prev_llbb - // - // At this point, `popped_scopes` is empty, and so the final block - // that we return to the user is `Cleanup(AST 24)`. - while let Some(mut scope) = popped_scopes.pop() { - if !scope.cleanups.is_empty() { - let name = scope.block_name("clean"); - debug!("generating cleanups for {}", name); - - let bcx_in = self.new_block(&name[..]); - let exit_label = label.start(bcx_in); - let mut bcx_out = bcx_in; - let len = scope.cleanups.len(); - for cleanup in scope.cleanups.iter().rev().take(len - skip) { - bcx_out = cleanup.trans(bcx_out, scope.debug_loc); - } - skip = 0; - exit_label.branch(bcx_out, prev_llbb); - prev_llbb = bcx_in.llbb; - - scope.add_cached_early_exit(exit_label, prev_llbb, len); - } - self.push_scope(scope); - } - - debug!("trans_cleanups_to_exit_scope: prev_llbb={:?}", prev_llbb); - - assert_eq!(self.scopes_len(), orig_scopes_len); - prev_llbb - } - - /// Creates a landing pad for the top scope, if one does not exist. The - /// landing pad will perform all cleanups necessary for an unwind and then - /// `resume` to continue error propagation: - /// - /// landing_pad -> ... cleanups ... -> [resume] - /// - /// (The cleanups and resume instruction are created by - /// `trans_cleanups_to_exit_scope()`, not in this function itself.) - fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef { - let pad_bcx; - - debug!("get_or_create_landing_pad"); - - // Check if a landing pad block exists; if not, create one. - { - let mut scopes = self.scopes.borrow_mut(); - let last_scope = scopes.last_mut().unwrap(); - match last_scope.cached_landing_pad { - Some(llbb) => return llbb, - None => { - let name = last_scope.block_name("unwind"); - pad_bcx = self.new_block(&name[..]); - last_scope.cached_landing_pad = Some(pad_bcx.llbb); - } - } - }; - - let llpersonality = pad_bcx.fcx.eh_personality(); - - let val = if base::wants_msvc_seh(self.ccx.sess()) { - // A cleanup pad requires a personality function to be specified, so - // we do that here explicitly (happens implicitly below through - // creation of the landingpad instruction). We then create a - // cleanuppad instruction which has no filters to run cleanup on all - // exceptions. - build::SetPersonalityFn(pad_bcx, llpersonality); - let llretval = build::CleanupPad(pad_bcx, None, &[]); - UnwindKind::CleanupPad(llretval) - } else { - // The landing pad return type (the type being propagated). Not sure - // what this represents but it's determined by the personality - // function and this is what the EH proposal example uses. - let llretty = Type::struct_(self.ccx, - &[Type::i8p(self.ccx), Type::i32(self.ccx)], - false); - - // The only landing pad clause will be 'cleanup' - let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1); - - // The landing pad block is a cleanup - build::SetCleanup(pad_bcx, llretval); - - let addr = match self.landingpad_alloca.get() { - Some(addr) => addr, - None => { - let addr = base::alloca(pad_bcx, common::val_ty(llretval), - ""); - base::call_lifetime_start(pad_bcx, addr); - self.landingpad_alloca.set(Some(addr)); - addr - } - }; - build::Store(pad_bcx, llretval, addr); - UnwindKind::LandingPad - }; - - // Generate the cleanup block and branch to it. - let label = UnwindExit(val); - let cleanup_llbb = self.trans_cleanups_to_exit_scope(label); - label.branch(pad_bcx, cleanup_llbb); - - return pad_bcx.llbb; + CleanupScope::new(self, drop) } } impl<'tcx> CleanupScope<'tcx> { - fn new(debug_loc: DebugLoc) -> CleanupScope<'tcx> { + fn new<'a>(fcx: &FunctionContext<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> { CleanupScope { - debug_loc: debug_loc, - cleanups: vec![], - cached_early_exits: vec![], - cached_landing_pad: None, - } - } - - fn cached_early_exit(&self, - label: EarlyExitLabel) - -> Option<(BasicBlockRef, usize)> { - self.cached_early_exits.iter().rev(). - find(|e| e.label == label). - map(|e| (e.cleanup_block, e.last_cleanup)) - } - - fn add_cached_early_exit(&mut self, - label: EarlyExitLabel, - blk: BasicBlockRef, - last_cleanup: usize) { - self.cached_early_exits.push( - CachedEarlyExit { label: label, - cleanup_block: blk, - last_cleanup: last_cleanup}); - } - - /// True if this scope has cleanups that need unwinding - fn needs_invoke(&self) -> bool { - self.cached_landing_pad.is_some() || - !self.cleanups.is_empty() - } - - /// Returns a suitable name to use for the basic block that handles this cleanup scope - fn block_name(&self, prefix: &str) -> String { - format!("{}_custom_", prefix) - } -} - -impl EarlyExitLabel { - /// Generates a branch going from `from_bcx` to `to_llbb` where `self` is - /// the exit label attached to the start of `from_bcx`. - /// - /// Transitions from an exit label to other exit labels depend on the type - /// of label. For example with MSVC exceptions unwind exit labels will use - /// the `cleanupret` instruction instead of the `br` instruction. - fn branch(&self, from_bcx: Block, to_llbb: BasicBlockRef) { - if let UnwindExit(UnwindKind::CleanupPad(pad)) = *self { - build::CleanupRet(from_bcx, pad, Some(to_llbb)); - } else { - build::Br(from_bcx, to_llbb, DebugLoc::None); + cleanup: Some(drop_val), + landing_pad: if !fcx.ccx.sess().no_landing_pads() { + Some(drop_val.get_landing_pad(fcx)) + } else { + None + }, } } - /// Generates the necessary instructions at the start of `bcx` to prepare - /// for the same kind of early exit label that `self` is. - /// - /// This function will appropriately configure `bcx` based on the kind of - /// label this is. For UnwindExit labels, the `lpad` field of the block will - /// be set to `Some`, and for MSVC exceptions this function will generate a - /// `cleanuppad` instruction at the start of the block so it may be jumped - /// to in the future (e.g. so this block can be cached as an early exit). - /// - /// Returns a new label which will can be used to cache `bcx` in the list of - /// early exits. - fn start(&self, bcx: Block) -> EarlyExitLabel { - match *self { - UnwindExit(UnwindKind::CleanupPad(..)) => { - let pad = build::CleanupPad(bcx, None, &[]); - bcx.lpad.set(Some(bcx.fcx.lpad_arena.alloc(LandingPad::msvc(pad)))); - UnwindExit(UnwindKind::CleanupPad(pad)) - } - UnwindExit(UnwindKind::LandingPad) => { - bcx.lpad.set(Some(bcx.fcx.lpad_arena.alloc(LandingPad::gnu()))); - *self - } + pub fn noop() -> CleanupScope<'tcx> { + CleanupScope { + cleanup: None, + landing_pad: None, } } -} -impl PartialEq for UnwindKind { - fn eq(&self, val: &UnwindKind) -> bool { - match (*self, *val) { - (UnwindKind::LandingPad, UnwindKind::LandingPad) | - (UnwindKind::CleanupPad(..), UnwindKind::CleanupPad(..)) => true, - _ => false, + pub fn trans<'a>(self, bcx: &'a BlockAndBuilder<'a, 'tcx>) { + if let Some(cleanup) = self.cleanup { + cleanup.trans(None, &bcx); } } } - -/////////////////////////////////////////////////////////////////////////// -// Cleanup types - -#[derive(Copy, Clone)] -pub struct DropValue<'tcx> { - is_immediate: bool, - val: ValueRef, - ty: Ty<'tcx>, - skip_dtor: bool, -} - -impl<'tcx> DropValue<'tcx> { - fn trans<'blk>(&self, - bcx: Block<'blk, 'tcx>, - debug_loc: DebugLoc) - -> Block<'blk, 'tcx> { - let skip_dtor = self.skip_dtor; - let _icx = if skip_dtor { - base::push_ctxt("<DropValue as Cleanup>::trans skip_dtor=true") - } else { - base::push_ctxt("<DropValue as Cleanup>::trans skip_dtor=false") - }; - let bcx = if self.is_immediate { - glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc, self.skip_dtor) - } else { - glue::drop_ty_core(bcx, self.val, self.ty, debug_loc, self.skip_dtor) - }; - bcx - } -} diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 3af3ada66b3..84222bfe56e 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -193,9 +193,9 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map as hir_map; use rustc::hir::def_id::DefId; -use rustc::middle::lang_items::{ExchangeFreeFnLangItem, ExchangeMallocFnLangItem}; +use rustc::middle::lang_items::{BoxFreeFnLangItem, ExchangeMallocFnLangItem}; use rustc::traits; -use rustc::ty::subst::{Substs, Subst}; +use rustc::ty::subst::{Kind, Substs, Subst}; use rustc::ty::{self, TypeFoldable, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::mir::{self, Location}; @@ -208,13 +208,15 @@ use syntax::abi::Abi; use syntax_pos::DUMMY_SP; use base::custom_coerce_unsize_info; use context::SharedCrateContext; -use common::{fulfill_obligation, type_is_sized}; +use common::fulfill_obligation; use glue::{self, DropGlueKind}; use monomorphize::{self, Instance}; use util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; use trans_item::{TransItem, DefPathBasedNames}; +use std::iter; + #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub enum TransItemCollectionMode { Eager, @@ -337,7 +339,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, TransItem::Static(node_id) => { let def_id = scx.tcx().map.local_def_id(node_id); let ty = scx.tcx().item_type(def_id); - let ty = glue::get_drop_glue_type(scx.tcx(), ty); + let ty = glue::get_drop_glue_type(scx, ty); neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); recursion_depth_reset = None; @@ -542,7 +544,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.param_substs, &ty); assert!(ty.is_normalized_for_trans()); - let ty = glue::get_drop_glue_type(self.scx.tcx(), ty); + let ty = glue::get_drop_glue_type(self.scx, ty); self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); } @@ -678,7 +680,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let operand_ty = monomorphize::apply_param_substs(self.scx, self.param_substs, &mt.ty); - let ty = glue::get_drop_glue_type(tcx, operand_ty); + let ty = glue::get_drop_glue_type(self.scx, operand_ty); self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); } else { bug!("Has the drop_in_place() intrinsic's signature changed?") @@ -723,23 +725,17 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, debug!("find_drop_glue_neighbors: {}", type_to_string(scx.tcx(), ty)); - // Make sure the exchange_free_fn() lang-item gets translated if - // there is a boxed value. - if let ty::TyBox(_) = ty.sty { - let exchange_free_fn_def_id = scx.tcx() - .lang_items - .require(ExchangeFreeFnLangItem) - .unwrap_or_else(|e| scx.sess().fatal(&e)); - - assert!(can_have_local_instance(scx.tcx(), exchange_free_fn_def_id)); - let fn_substs = scx.empty_substs_for_def_id(exchange_free_fn_def_id); - let exchange_free_fn_trans_item = + // Make sure the BoxFreeFn lang-item gets translated if there is a boxed value. + if let ty::TyBox(content_type) = ty.sty { + let def_id = scx.tcx().require_lang_item(BoxFreeFnLangItem); + assert!(can_have_local_instance(scx.tcx(), def_id)); + let box_free_fn_trans_item = create_fn_trans_item(scx, - exchange_free_fn_def_id, - fn_substs, + def_id, + scx.tcx().mk_substs(iter::once(Kind::from(content_type))), scx.tcx().intern_substs(&[])); - output.push(exchange_free_fn_trans_item); + output.push(box_free_fn_trans_item); } // If the type implements Drop, also add a translation item for the @@ -804,17 +800,17 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, let field_type = monomorphize::apply_param_substs(scx, substs, &field_type); - let field_type = glue::get_drop_glue_type(scx.tcx(), field_type); + let field_type = glue::get_drop_glue_type(scx, field_type); - if glue::type_needs_drop(scx.tcx(), field_type) { + if scx.type_needs_drop(field_type) { output.push(TransItem::DropGlue(DropGlueKind::Ty(field_type))); } } } ty::TyClosure(def_id, substs) => { for upvar_ty in substs.upvar_tys(def_id, scx.tcx()) { - let upvar_ty = glue::get_drop_glue_type(scx.tcx(), upvar_ty); - if glue::type_needs_drop(scx.tcx(), upvar_ty) { + let upvar_ty = glue::get_drop_glue_type(scx, upvar_ty); + if scx.type_needs_drop(upvar_ty) { output.push(TransItem::DropGlue(DropGlueKind::Ty(upvar_ty))); } } @@ -822,15 +818,15 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, ty::TyBox(inner_type) | ty::TySlice(inner_type) | ty::TyArray(inner_type, _) => { - let inner_type = glue::get_drop_glue_type(scx.tcx(), inner_type); - if glue::type_needs_drop(scx.tcx(), inner_type) { + let inner_type = glue::get_drop_glue_type(scx, inner_type); + if scx.type_needs_drop(inner_type) { output.push(TransItem::DropGlue(DropGlueKind::Ty(inner_type))); } } ty::TyTuple(args) => { for arg in args { - let arg = glue::get_drop_glue_type(scx.tcx(), arg); - if glue::type_needs_drop(scx.tcx(), arg) { + let arg = glue::get_drop_glue_type(scx, arg); + if scx.type_needs_drop(arg) { output.push(TransItem::DropGlue(DropGlueKind::Ty(arg))); } } @@ -969,7 +965,7 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { let (inner_source, inner_target) = (a, b); - if !type_is_sized(scx.tcx(), inner_source) { + if !scx.type_is_sized(inner_source) { (inner_source, inner_target) } else { scx.tcx().struct_lockstep_tails(inner_source, inner_target) @@ -1051,7 +1047,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, output.extend(methods); } // Also add the destructor - let dg_type = glue::get_drop_glue_type(scx.tcx(), impl_ty); + let dg_type = glue::get_drop_glue_type(scx, impl_ty); output.push(TransItem::DropGlue(DropGlueKind::Ty(dg_type))); } } @@ -1097,7 +1093,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> { def_id_to_string(self.scx.tcx(), def_id)); let ty = self.scx.tcx().item_type(def_id); - let ty = glue::get_drop_glue_type(self.scx.tcx(), ty); + let ty = glue::get_drop_glue_type(self.scx, ty); self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); } } diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index b1d61cea39c..71e17f1ea74 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -14,24 +14,16 @@ use session::Session; use llvm; -use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef, TypeKind}; +use llvm::{ValueRef, BasicBlockRef, ContextRef, TypeKind}; use llvm::{True, False, Bool, OperandBundleDef}; use rustc::hir::def::Def; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; -use rustc::infer::TransNormalize; -use rustc::mir::Mir; use rustc::util::common::MemoizationMap; use middle::lang_items::LangItem; -use rustc::ty::subst::Substs; -use abi::{Abi, FnType}; use base; -use build; use builder::Builder; -use callee::Callee; -use cleanup; use consts; -use debuginfo::{self, DebugLoc}; use declare; use machine; use monomorphize; @@ -40,34 +32,26 @@ use value::Value; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::layout::Layout; use rustc::traits::{self, SelectionContext, Reveal}; -use rustc::ty::fold::TypeFoldable; use rustc::hir; -use arena::TypedArena; use libc::{c_uint, c_char}; use std::borrow::Cow; use std::iter; use std::ops::Deref; use std::ffi::CString; -use std::cell::{Cell, RefCell, Ref}; use syntax::ast; use syntax::symbol::{Symbol, InternedString}; -use syntax_pos::{DUMMY_SP, Span}; +use syntax_pos::Span; pub use context::{CrateContext, SharedCrateContext}; -/// Is the type's representation size known at compile time? -pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_sized(tcx, &tcx.empty_parameter_environment(), DUMMY_SP) -} - -pub fn type_is_fat_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { +pub fn type_is_fat_ptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { match ty.sty { ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | ty::TyRef(_, ty::TypeAndMut{ty, ..}) | ty::TyBox(ty) => { - !type_is_sized(tcx, ty) + !ccx.shared().type_is_sized(ty) } _ => { false @@ -79,14 +63,13 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) - use machine::llsize_of_alloc; use type_of::sizing_type_of; - let tcx = ccx.tcx(); let simple = ty.is_scalar() || ty.is_unique() || ty.is_region_ptr() || ty.is_simd(); - if simple && !type_is_fat_ptr(tcx, ty) { + if simple && !type_is_fat_ptr(ccx, ty) { return true; } - if !type_is_sized(tcx, ty) { + if !ccx.shared().type_is_sized(ty) { return false; } match ty.sty { @@ -236,416 +219,139 @@ impl<'a, 'tcx> VariantInfo<'tcx> { } } -pub struct BuilderRef_res { - pub b: BuilderRef, -} - -impl Drop for BuilderRef_res { - fn drop(&mut self) { - unsafe { - llvm::LLVMDisposeBuilder(self.b); - } - } -} - -pub fn BuilderRef_res(b: BuilderRef) -> BuilderRef_res { - BuilderRef_res { - b: b - } -} - -pub fn validate_substs(substs: &Substs) { - assert!(!substs.needs_infer()); -} - -// Function context. Every LLVM function we create will have one of -// these. +// Function context. Every LLVM function we create will have one of these. pub struct FunctionContext<'a, 'tcx: 'a> { - // The MIR for this function. - pub mir: Option<Ref<'tcx, Mir<'tcx>>>, - // The ValueRef returned from a call to llvm::LLVMAddFunction; the // address of the first instruction in the sequence of // instructions for this function that will go in the .text // section of the executable we're generating. pub llfn: ValueRef, - // always an empty parameter-environment NOTE: @jroesch another use of ParamEnv - pub param_env: ty::ParameterEnvironment<'tcx>, - - // A pointer to where to store the return value. If the return type is - // immediate, this points to an alloca in the function. Otherwise, it's a - // pointer to the hidden first parameter of the function. After function - // construction, this should always be Some. - pub llretslotptr: Cell<Option<ValueRef>>, - - // These pub elements: "hoisted basic blocks" containing - // administrative activities that have to happen in only one place in - // the function, due to LLVM's quirks. // A marker for the place where we want to insert the function's static // allocas, so that LLVM will coalesce them into a single alloca call. - pub alloca_insert_pt: Cell<Option<ValueRef>>, - - // When working with landingpad-based exceptions this value is alloca'd and - // later loaded when using the resume instruction. This ends up being - // critical to chaining landing pads and resuing already-translated - // cleanups. - // - // Note that for cleanuppad-based exceptions this is not used. - pub landingpad_alloca: Cell<Option<ValueRef>>, - - // Describes the return/argument LLVM types and their ABI handling. - pub fn_ty: FnType, - - // If this function is being monomorphized, this contains the type - // substitutions used. - pub param_substs: &'tcx Substs<'tcx>, - - // The source span and nesting context where this function comes from, for - // error reporting and symbol generation. - pub span: Option<Span>, - - // The arena that blocks are allocated from. - pub block_arena: &'a TypedArena<BlockS<'a, 'tcx>>, - - // The arena that landing pads are allocated from. - pub lpad_arena: TypedArena<LandingPad>, + alloca_insert_pt: Option<ValueRef>, // This function's enclosing crate context. pub ccx: &'a CrateContext<'a, 'tcx>, - // Used and maintained by the debuginfo module. - pub debug_context: debuginfo::FunctionDebugContext, - - // Cleanup scopes. - pub scopes: RefCell<Vec<cleanup::CleanupScope<'tcx>>>, + alloca_builder: Builder<'a, 'tcx>, } impl<'a, 'tcx> FunctionContext<'a, 'tcx> { - pub fn mir(&self) -> Ref<'tcx, Mir<'tcx>> { - self.mir.as_ref().map(Ref::clone).expect("fcx.mir was empty") + /// Create a function context for the given function. + /// Call FunctionContext::get_entry_block for the first entry block. + pub fn new(ccx: &'a CrateContext<'a, 'tcx>, llfndecl: ValueRef) -> FunctionContext<'a, 'tcx> { + let mut fcx = FunctionContext { + llfn: llfndecl, + alloca_insert_pt: None, + ccx: ccx, + alloca_builder: Builder::with_ccx(ccx), + }; + + let val = { + let entry_bcx = fcx.build_new_block("entry-block"); + let val = entry_bcx.load(C_null(Type::i8p(ccx))); + fcx.alloca_builder.position_at_start(entry_bcx.llbb()); + val + }; + + // Use a dummy instruction as the insertion point for all allocas. + // This is later removed in the drop of FunctionContext. + fcx.alloca_insert_pt = Some(val); + + fcx } - pub fn cleanup(&self) { - unsafe { - llvm::LLVMInstructionEraseFromParent(self.alloca_insert_pt - .get() - .unwrap()); - } + pub fn get_entry_block(&'a self) -> BlockAndBuilder<'a, 'tcx> { + BlockAndBuilder::new(unsafe { + llvm::LLVMGetFirstBasicBlock(self.llfn) + }, self) } - pub fn new_block(&'a self, - name: &str) - -> Block<'a, 'tcx> { + pub fn new_block(&'a self, name: &str) -> BasicBlockRef { unsafe { let name = CString::new(name).unwrap(); - let llbb = llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), - self.llfn, - name.as_ptr()); - BlockS::new(llbb, self) + llvm::LLVMAppendBasicBlockInContext( + self.ccx.llcx(), + self.llfn, + name.as_ptr() + ) } } - pub fn monomorphize<T>(&self, value: &T) -> T - where T: TransNormalize<'tcx> - { - monomorphize::apply_param_substs(self.ccx.shared(), - self.param_substs, - value) - } - - /// This is the same as `common::type_needs_drop`, except that it - /// may use or update caches within this `FunctionContext`. - pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - self.ccx.tcx().type_needs_drop_given_env(ty, &self.param_env) + pub fn build_new_block(&'a self, name: &str) -> BlockAndBuilder<'a, 'tcx> { + BlockAndBuilder::new(self.new_block(name), self) } - pub fn eh_personality(&self) -> ValueRef { - // The exception handling personality function. - // - // If our compilation unit has the `eh_personality` lang item somewhere - // within it, then we just need to translate that. Otherwise, we're - // building an rlib which will depend on some upstream implementation of - // this function, so we just codegen a generic reference to it. We don't - // specify any of the types for the function, we just make it a symbol - // that LLVM can later use. - // - // Note that MSVC is a little special here in that we don't use the - // `eh_personality` lang item at all. Currently LLVM has support for - // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the - // *name of the personality function* to decide what kind of unwind side - // tables/landing pads to emit. It looks like Dwarf is used by default, - // injecting a dependency on the `_Unwind_Resume` symbol for resuming - // an "exception", but for MSVC we want to force SEH. This means that we - // can't actually have the personality function be our standard - // `rust_eh_personality` function, but rather we wired it up to the - // CRT's custom personality function, which forces LLVM to consider - // landing pads as "landing pads for SEH". - let ccx = self.ccx; - let tcx = ccx.tcx(); - match tcx.lang_items.eh_personality() { - Some(def_id) if !base::wants_msvc_seh(ccx.sess()) => { - Callee::def(ccx, def_id, tcx.intern_substs(&[])).reify(ccx) - } - _ => { - if let Some(llpersonality) = ccx.eh_personality().get() { - return llpersonality - } - let name = if base::wants_msvc_seh(ccx.sess()) { - "__CxxFrameHandler3" - } else { - "rust_eh_personality" - }; - let fty = Type::variadic_func(&[], &Type::i32(ccx)); - let f = declare::declare_cfn(ccx, name, fty); - ccx.eh_personality().set(Some(f)); - f - } - } + pub fn alloca(&self, ty: Type, name: &str) -> ValueRef { + self.alloca_builder.dynamic_alloca(ty, name) } +} - // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined, - // otherwise declares it as an external function. - pub fn eh_unwind_resume(&self) -> Callee<'tcx> { - use attributes; - let ccx = self.ccx; - let tcx = ccx.tcx(); - assert!(ccx.sess().target.target.options.custom_unwind_resume); - if let Some(def_id) = tcx.lang_items.eh_unwind_resume() { - return Callee::def(ccx, def_id, tcx.intern_substs(&[])); - } - - let ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy { - unsafety: hir::Unsafety::Unsafe, - abi: Abi::C, - sig: ty::Binder(tcx.mk_fn_sig( - iter::once(tcx.mk_mut_ptr(tcx.types.u8)), - tcx.types.never, - false - )), - })); - - let unwresume = ccx.eh_unwind_resume(); - if let Some(llfn) = unwresume.get() { - return Callee::ptr(llfn, ty); +impl<'a, 'tcx> Drop for FunctionContext<'a, 'tcx> { + fn drop(&mut self) { + unsafe { + llvm::LLVMInstructionEraseFromParent(self.alloca_insert_pt.unwrap()); } - let llfn = declare::declare_fn(ccx, "rust_eh_unwind_resume", ty); - attributes::unwind(llfn, true); - unwresume.set(Some(llfn)); - Callee::ptr(llfn, ty) } } -// Basic block context. We create a block context for each basic block -// (single-entry, single-exit sequence of instructions) we generate from Rust -// code. Each basic block we generate is attached to a function, typically -// with many basic blocks per function. All the basic blocks attached to a -// function are organized as a directed graph. -pub struct BlockS<'blk, 'tcx: 'blk> { +#[must_use] +pub struct BlockAndBuilder<'a, 'tcx: 'a> { // The BasicBlockRef returned from a call to // llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic // block to the function pointed to by llfn. We insert // instructions into that block by way of this block context. // The block pointing to this one in the function's digraph. - pub llbb: BasicBlockRef, - pub terminated: Cell<bool>, - pub unreachable: Cell<bool>, - - // If this block part of a landing pad, then this is `Some` indicating what - // kind of landing pad its in, otherwise this is none. - pub lpad: Cell<Option<&'blk LandingPad>>, + llbb: BasicBlockRef, // The function context for the function to which this block is // attached. - pub fcx: &'blk FunctionContext<'blk, 'tcx>, -} - -pub type Block<'blk, 'tcx> = &'blk BlockS<'blk, 'tcx>; - -impl<'blk, 'tcx> BlockS<'blk, 'tcx> { - pub fn new(llbb: BasicBlockRef, - fcx: &'blk FunctionContext<'blk, 'tcx>) - -> Block<'blk, 'tcx> { - fcx.block_arena.alloc(BlockS { - llbb: llbb, - terminated: Cell::new(false), - unreachable: Cell::new(false), - lpad: Cell::new(None), - fcx: fcx - }) - } - - pub fn ccx(&self) -> &'blk CrateContext<'blk, 'tcx> { - self.fcx.ccx - } - pub fn fcx(&self) -> &'blk FunctionContext<'blk, 'tcx> { - self.fcx - } - pub fn tcx(&self) -> TyCtxt<'blk, 'tcx, 'tcx> { - self.fcx.ccx.tcx() - } - pub fn sess(&self) -> &'blk Session { self.fcx.ccx.sess() } - - pub fn lpad(&self) -> Option<&'blk LandingPad> { - self.lpad.get() - } - - pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) { - // FIXME: use an IVar? - self.lpad.set(lpad); - } + fcx: &'a FunctionContext<'a, 'tcx>, - pub fn set_lpad(&self, lpad: Option<LandingPad>) { - self.set_lpad_ref(lpad.map(|p| &*self.fcx().lpad_arena.alloc(p))) - } - - pub fn mir(&self) -> Ref<'tcx, Mir<'tcx>> { - self.fcx.mir() - } - - pub fn name(&self, name: ast::Name) -> String { - name.to_string() - } - - pub fn node_id_to_string(&self, id: ast::NodeId) -> String { - self.tcx().map.node_to_string(id).to_string() - } - - pub fn to_str(&self) -> String { - format!("[block {:p}]", self) - } - - pub fn monomorphize<T>(&self, value: &T) -> T - where T: TransNormalize<'tcx> - { - monomorphize::apply_param_substs(self.fcx.ccx.shared(), - self.fcx.param_substs, - value) - } - - pub fn build(&'blk self) -> BlockAndBuilder<'blk, 'tcx> { - BlockAndBuilder::new(self, OwnedBuilder::new_with_ccx(self.ccx())) - } -} - -pub struct OwnedBuilder<'blk, 'tcx: 'blk> { - builder: Builder<'blk, 'tcx> + builder: Builder<'a, 'tcx>, } -impl<'blk, 'tcx> OwnedBuilder<'blk, 'tcx> { - pub fn new_with_ccx(ccx: &'blk CrateContext<'blk, 'tcx>) -> Self { - // Create a fresh builder from the crate context. - let llbuilder = unsafe { - llvm::LLVMCreateBuilderInContext(ccx.llcx()) - }; - OwnedBuilder { - builder: Builder { - llbuilder: llbuilder, - ccx: ccx, - } - } - } -} - -impl<'blk, 'tcx> Drop for OwnedBuilder<'blk, 'tcx> { - fn drop(&mut self) { - unsafe { - llvm::LLVMDisposeBuilder(self.builder.llbuilder); - } - } -} - -pub struct BlockAndBuilder<'blk, 'tcx: 'blk> { - bcx: Block<'blk, 'tcx>, - owned_builder: OwnedBuilder<'blk, 'tcx>, -} - -impl<'blk, 'tcx> BlockAndBuilder<'blk, 'tcx> { - pub fn new(bcx: Block<'blk, 'tcx>, owned_builder: OwnedBuilder<'blk, 'tcx>) -> Self { +impl<'a, 'tcx> BlockAndBuilder<'a, 'tcx> { + pub fn new(llbb: BasicBlockRef, fcx: &'a FunctionContext<'a, 'tcx>) -> Self { + let builder = Builder::with_ccx(fcx.ccx); // Set the builder's position to this block's end. - owned_builder.builder.position_at_end(bcx.llbb); + builder.position_at_end(llbb); BlockAndBuilder { - bcx: bcx, - owned_builder: owned_builder, + llbb: llbb, + fcx: fcx, + builder: builder, } } - pub fn with_block<F, R>(&self, f: F) -> R - where F: FnOnce(Block<'blk, 'tcx>) -> R - { - let result = f(self.bcx); - self.position_at_end(self.bcx.llbb); - result - } - - pub fn map_block<F>(self, f: F) -> Self - where F: FnOnce(Block<'blk, 'tcx>) -> Block<'blk, 'tcx> - { - let BlockAndBuilder { bcx, owned_builder } = self; - let bcx = f(bcx); - BlockAndBuilder::new(bcx, owned_builder) - } - pub fn at_start<F, R>(&self, f: F) -> R - where F: FnOnce(&BlockAndBuilder<'blk, 'tcx>) -> R + where F: FnOnce(&BlockAndBuilder<'a, 'tcx>) -> R { - self.position_at_start(self.bcx.llbb); + self.position_at_start(self.llbb); let r = f(self); - self.position_at_end(self.bcx.llbb); + self.position_at_end(self.llbb); r } - // Methods delegated to bcx - - pub fn is_unreachable(&self) -> bool { - self.bcx.unreachable.get() - } - - pub fn ccx(&self) -> &'blk CrateContext<'blk, 'tcx> { - self.bcx.ccx() - } - pub fn fcx(&self) -> &'blk FunctionContext<'blk, 'tcx> { - self.bcx.fcx() + pub fn fcx(&self) -> &'a FunctionContext<'a, 'tcx> { + self.fcx } - pub fn tcx(&self) -> TyCtxt<'blk, 'tcx, 'tcx> { - self.bcx.tcx() + pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.ccx.tcx() } - pub fn sess(&self) -> &'blk Session { - self.bcx.sess() + pub fn sess(&self) -> &'a Session { + self.ccx.sess() } pub fn llbb(&self) -> BasicBlockRef { - self.bcx.llbb - } - - pub fn mir(&self) -> Ref<'tcx, Mir<'tcx>> { - self.bcx.mir() - } - - pub fn monomorphize<T>(&self, value: &T) -> T - where T: TransNormalize<'tcx> - { - self.bcx.monomorphize(value) - } - - pub fn set_lpad(&self, lpad: Option<LandingPad>) { - self.bcx.set_lpad(lpad) - } - - pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) { - // FIXME: use an IVar? - self.bcx.set_lpad_ref(lpad); - } - - pub fn lpad(&self) -> Option<&'blk LandingPad> { - self.bcx.lpad() + self.llbb } } -impl<'blk, 'tcx> Deref for BlockAndBuilder<'blk, 'tcx> { - type Target = Builder<'blk, 'tcx>; +impl<'a, 'tcx> Deref for BlockAndBuilder<'a, 'tcx> { + type Target = Builder<'a, 'tcx>; fn deref(&self) -> &Self::Target { - &self.owned_builder.builder + &self.builder } } @@ -663,53 +369,33 @@ impl<'blk, 'tcx> Deref for BlockAndBuilder<'blk, 'tcx> { /// When inside of a landing pad, each function call in LLVM IR needs to be /// annotated with which landing pad it's a part of. This is accomplished via /// the `OperandBundleDef` value created for MSVC landing pads. -pub struct LandingPad { - cleanuppad: Option<ValueRef>, - operand: Option<OperandBundleDef>, +pub struct Funclet { + cleanuppad: ValueRef, + operand: OperandBundleDef, } -impl LandingPad { - pub fn gnu() -> LandingPad { - LandingPad { cleanuppad: None, operand: None } - } - - pub fn msvc(cleanuppad: ValueRef) -> LandingPad { - LandingPad { - cleanuppad: Some(cleanuppad), - operand: Some(OperandBundleDef::new("funclet", &[cleanuppad])), +impl Funclet { + pub fn new(cleanuppad: ValueRef) -> Funclet { + Funclet { + cleanuppad: cleanuppad, + operand: OperandBundleDef::new("funclet", &[cleanuppad]), } } - pub fn bundle(&self) -> Option<&OperandBundleDef> { - self.operand.as_ref() - } - - pub fn cleanuppad(&self) -> Option<ValueRef> { + pub fn cleanuppad(&self) -> ValueRef { self.cleanuppad } -} -impl Clone for LandingPad { - fn clone(&self) -> LandingPad { - LandingPad { - cleanuppad: self.cleanuppad, - operand: self.cleanuppad.map(|p| { - OperandBundleDef::new("funclet", &[p]) - }), - } + pub fn bundle(&self) -> &OperandBundleDef { + &self.operand } } -pub struct Result<'blk, 'tcx: 'blk> { - pub bcx: Block<'blk, 'tcx>, - pub val: ValueRef -} - -impl<'b, 'tcx> Result<'b, 'tcx> { - pub fn new(bcx: Block<'b, 'tcx>, val: ValueRef) -> Result<'b, 'tcx> { - Result { - bcx: bcx, - val: val, +impl Clone for Funclet { + fn clone(&self) -> Funclet { + Funclet { + cleanuppad: self.cleanuppad, + operand: OperandBundleDef::new("funclet", &[self.cleanuppad]), } } } @@ -1016,43 +702,42 @@ pub fn langcall(tcx: TyCtxt, // all shifts). For 32- and 64-bit types, this matches the semantics // of Java. (See related discussion on #1877 and #10183.) -pub fn build_unchecked_lshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - binop_debug_loc: DebugLoc) -> ValueRef { +pub fn build_unchecked_lshift<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + lhs: ValueRef, + rhs: ValueRef +) -> ValueRef { let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShl, lhs, rhs); // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); - build::Shl(bcx, lhs, rhs, binop_debug_loc) + let rhs = shift_mask_rhs(bcx, rhs); + bcx.shl(lhs, rhs) } -pub fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - lhs_t: Ty<'tcx>, - lhs: ValueRef, - rhs: ValueRef, - binop_debug_loc: DebugLoc) -> ValueRef { +pub fn build_unchecked_rshift<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef +) -> ValueRef { let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs); // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); + let rhs = shift_mask_rhs(bcx, rhs); let is_signed = lhs_t.is_signed(); if is_signed { - build::AShr(bcx, lhs, rhs, binop_debug_loc) + bcx.ashr(lhs, rhs) } else { - build::LShr(bcx, lhs, rhs, binop_debug_loc) + bcx.lshr(lhs, rhs) } } -fn shift_mask_rhs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - rhs: ValueRef, - debug_loc: DebugLoc) -> ValueRef { +fn shift_mask_rhs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, rhs: ValueRef) -> ValueRef { let rhs_llty = val_ty(rhs); - build::And(bcx, rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false), debug_loc) + bcx.and(rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false)) } -pub fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - llty: Type, - mask_llty: Type, - invert: bool) -> ValueRef { +pub fn shift_mask_val<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + llty: Type, + mask_llty: Type, + invert: bool +) -> ValueRef { let kind = llty.kind(); match kind { TypeKind::Integer => { @@ -1066,7 +751,7 @@ pub fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, }, TypeKind::Vector => { let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert); - build::VectorSplat(bcx, mask_llty.vector_length(), mask) + bcx.vector_splat(mask_llty.vector_length(), mask) }, _ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind), } diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index 730a4025a59..2e2644d91bb 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -16,7 +16,7 @@ use rustc_const_eval::ConstEvalErr; use rustc::hir::def_id::DefId; use rustc::hir::map as hir_map; use {debuginfo, machine}; -use base::{self, push_ctxt}; +use base; use trans_item::TransItem; use common::{CrateContext, val_ty}; use declare; @@ -221,7 +221,6 @@ pub fn trans_static(ccx: &CrateContext, attrs: &[ast::Attribute]) -> Result<ValueRef, ConstEvalErr> { unsafe { - let _icx = push_ctxt("trans_static"); let def_id = ccx.tcx().map.local_def_id(id); let g = get_static(ccx, def_id); diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index 8b98eb57814..f292a709650 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -9,17 +9,16 @@ // except according to those terms. use llvm; -use llvm::{ContextRef, ModuleRef, ValueRef, BuilderRef}; -use rustc::dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig, - WorkProduct}; +use llvm::{ContextRef, ModuleRef, ValueRef}; +use rustc::dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig, WorkProduct}; use middle::cstore::LinkMeta; +use rustc::hir; use rustc::hir::def::ExportMap; use rustc::hir::def_id::DefId; use rustc::traits; -use base; -use builder::Builder; -use common::BuilderRef_res; use debuginfo; +use callee::Callee; +use base; use declare; use glue::DropGlueKind; use monomorphize::Instance; @@ -40,11 +39,13 @@ use std::ffi::{CStr, CString}; use std::cell::{Cell, RefCell}; use std::marker::PhantomData; use std::ptr; +use std::iter; use std::rc::Rc; use std::str; use syntax::ast; use syntax::symbol::InternedString; -use abi::FnType; +use syntax_pos::DUMMY_SP; +use abi::{Abi, FnType}; pub struct Stats { pub n_glues_created: Cell<usize>, @@ -71,6 +72,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> { exported_symbols: NodeSet, link_meta: LinkMeta, tcx: TyCtxt<'a, 'tcx, 'tcx>, + empty_param_env: ty::ParameterEnvironment<'tcx>, stats: Stats, check_overflow: bool, @@ -140,7 +142,6 @@ pub struct LocalCrateContext<'tcx> { int_type: Type, opaque_vec_type: Type, str_slice_type: Type, - builder: BuilderRef_res, /// Holds the LLVM values for closure IDs. closure_vals: RefCell<FxHashMap<Instance<'tcx>, ValueRef>>, @@ -153,11 +154,6 @@ pub struct LocalCrateContext<'tcx> { intrinsics: RefCell<FxHashMap<&'static str, ValueRef>>, - /// Number of LLVM instructions translated into this `LocalCrateContext`. - /// This is used to perform some basic load-balancing to keep all LLVM - /// contexts around the same size. - n_llvm_insns: Cell<usize>, - /// Depth of the current type-of computation - used to bail out type_of_depth: Cell<usize>, @@ -316,38 +312,6 @@ impl<'a, 'tcx> Iterator for CrateContextIterator<'a,'tcx> { } } -/// The iterator produced by `CrateContext::maybe_iter`. -pub struct CrateContextMaybeIterator<'a, 'tcx: 'a> { - shared: &'a SharedCrateContext<'a, 'tcx>, - local_ccxs: &'a [LocalCrateContext<'tcx>], - index: usize, - single: bool, - origin: usize, -} - -impl<'a, 'tcx> Iterator for CrateContextMaybeIterator<'a, 'tcx> { - type Item = (CrateContext<'a, 'tcx>, bool); - - fn next(&mut self) -> Option<(CrateContext<'a, 'tcx>, bool)> { - if self.index >= self.local_ccxs.len() { - return None; - } - - let index = self.index; - self.index += 1; - if self.single { - self.index = self.local_ccxs.len(); - } - - let ccx = CrateContext { - shared: self.shared, - index: index, - local_ccxs: self.local_ccxs - }; - Some((ccx, index == self.origin)) - } -} - pub fn get_reloc_model(sess: &Session) -> llvm::RelocMode { let reloc_model_arg = match sess.opts.cg.relocation_model { Some(ref s) => &s[..], @@ -496,6 +460,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { export_map: export_map, exported_symbols: exported_symbols, link_meta: link_meta, + empty_param_env: tcx.empty_parameter_environment(), tcx: tcx, stats: Stats { n_glues_created: Cell::new(0), @@ -516,6 +481,14 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { } } + pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { + self.tcx.type_needs_drop_given_env(ty, &self.empty_param_env) + } + + pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { + ty.is_sized(self.tcx, &self.empty_param_env, DUMMY_SP) + } + pub fn metadata_llmod(&self) -> ModuleRef { self.metadata_llmod } @@ -638,14 +611,12 @@ impl<'tcx> LocalCrateContext<'tcx> { int_type: Type::from_ref(ptr::null_mut()), opaque_vec_type: Type::from_ref(ptr::null_mut()), str_slice_type: Type::from_ref(ptr::null_mut()), - builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)), closure_vals: RefCell::new(FxHashMap()), dbg_cx: dbg_cx, eh_personality: Cell::new(None), eh_unwind_resume: Cell::new(None), rust_try_fn: Cell::new(None), intrinsics: RefCell::new(FxHashMap()), - n_llvm_insns: Cell::new(0), type_of_depth: Cell::new(0), symbol_map: symbol_map, local_gen_sym_counter: Cell::new(0), @@ -671,10 +642,6 @@ impl<'tcx> LocalCrateContext<'tcx> { local_ccx.opaque_vec_type = opaque_vec_type; local_ccx.str_slice_type = str_slice_ty; - if shared.tcx.sess.count_llvm_insns() { - base::init_insn_ctxt() - } - local_ccx } } @@ -703,26 +670,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { self.shared } - pub fn local(&self) -> &'b LocalCrateContext<'tcx> { + fn local(&self) -> &'b LocalCrateContext<'tcx> { &self.local_ccxs[self.index] } - /// Either iterate over only `self`, or iterate over all `CrateContext`s in - /// the `SharedCrateContext`. The iterator produces `(ccx, is_origin)` - /// pairs, where `is_origin` is `true` if `ccx` is `self` and `false` - /// otherwise. This method is useful for avoiding code duplication in - /// cases where it may or may not be necessary to translate code into every - /// context. - pub fn maybe_iter(&self, iter_all: bool) -> CrateContextMaybeIterator<'b, 'tcx> { - CrateContextMaybeIterator { - shared: self.shared, - index: if iter_all { 0 } else { self.index }, - single: !iter_all, - origin: self.index, - local_ccxs: self.local_ccxs, - } - } - pub fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { self.shared.tcx } @@ -731,14 +682,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.shared.tcx.sess } - pub fn builder<'a>(&'a self) -> Builder<'a, 'tcx> { - Builder::new(self) - } - - pub fn raw_builder<'a>(&'a self) -> BuilderRef { - self.local().builder.b - } - pub fn get_intrinsic(&self, key: &str) -> ValueRef { if let Some(v) = self.intrinsics().borrow().get(key).cloned() { return v; @@ -886,14 +829,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local().dbg_cx } - pub fn eh_personality<'a>(&'a self) -> &'a Cell<Option<ValueRef>> { - &self.local().eh_personality - } - - pub fn eh_unwind_resume<'a>(&'a self) -> &'a Cell<Option<ValueRef>> { - &self.local().eh_unwind_resume - } - pub fn rust_try_fn<'a>(&'a self) -> &'a Cell<Option<ValueRef>> { &self.local().rust_try_fn } @@ -902,10 +837,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local().intrinsics } - pub fn count_llvm_insn(&self) { - self.local().n_llvm_insns.set(self.local().n_llvm_insns.get() + 1); - } - pub fn obj_size_bound(&self) -> u64 { self.tcx().data_layout.obj_size_bound() } @@ -971,9 +902,85 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { let mut name = String::with_capacity(prefix.len() + 6); name.push_str(prefix); name.push_str("."); - base_n::push_str(idx as u64, base_n::MAX_BASE, &mut name); + base_n::push_str(idx as u64, base_n::ALPHANUMERIC_ONLY, &mut name); name } + + pub fn eh_personality(&self) -> ValueRef { + // The exception handling personality function. + // + // If our compilation unit has the `eh_personality` lang item somewhere + // within it, then we just need to translate that. Otherwise, we're + // building an rlib which will depend on some upstream implementation of + // this function, so we just codegen a generic reference to it. We don't + // specify any of the types for the function, we just make it a symbol + // that LLVM can later use. + // + // Note that MSVC is a little special here in that we don't use the + // `eh_personality` lang item at all. Currently LLVM has support for + // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the + // *name of the personality function* to decide what kind of unwind side + // tables/landing pads to emit. It looks like Dwarf is used by default, + // injecting a dependency on the `_Unwind_Resume` symbol for resuming + // an "exception", but for MSVC we want to force SEH. This means that we + // can't actually have the personality function be our standard + // `rust_eh_personality` function, but rather we wired it up to the + // CRT's custom personality function, which forces LLVM to consider + // landing pads as "landing pads for SEH". + if let Some(llpersonality) = self.local().eh_personality.get() { + return llpersonality + } + let tcx = self.tcx(); + let llfn = match tcx.lang_items.eh_personality() { + Some(def_id) if !base::wants_msvc_seh(self.sess()) => { + Callee::def(self, def_id, tcx.intern_substs(&[])).reify(self) + } + _ => { + let name = if base::wants_msvc_seh(self.sess()) { + "__CxxFrameHandler3" + } else { + "rust_eh_personality" + }; + let fty = Type::variadic_func(&[], &Type::i32(self)); + declare::declare_cfn(self, name, fty) + } + }; + self.local().eh_personality.set(Some(llfn)); + llfn + } + + // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined, + // otherwise declares it as an external function. + pub fn eh_unwind_resume(&self) -> ValueRef { + use attributes; + let unwresume = &self.local().eh_unwind_resume; + if let Some(llfn) = unwresume.get() { + return llfn; + } + + let tcx = self.tcx(); + assert!(self.sess().target.target.options.custom_unwind_resume); + if let Some(def_id) = tcx.lang_items.eh_unwind_resume() { + let llfn = Callee::def(self, def_id, tcx.intern_substs(&[])).reify(self); + unwresume.set(Some(llfn)); + return llfn; + } + + let ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy { + unsafety: hir::Unsafety::Unsafe, + abi: Abi::C, + sig: ty::Binder(tcx.mk_fn_sig( + iter::once(tcx.mk_mut_ptr(tcx.types.u8)), + tcx.types.never, + false + )), + })); + + let llfn = declare::declare_fn(self, "rust_eh_unwind_resume", ty); + attributes::unwind(llfn, true); + unwresume.set(Some(llfn)); + llfn + } } pub struct TypeOfDepthLock<'a, 'tcx: 'a>(&'a LocalCrateContext<'tcx>); diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs index e0c1a80be39..f5a8eeacf38 100644 --- a/src/librustc_trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/debuginfo/create_scope_map.rs @@ -44,8 +44,8 @@ impl MirDebugScope { /// Produce DIScope DIEs for each MIR Scope which has variables defined in it. /// If debuginfo is disabled, the returned vector is empty. -pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec<VisibilityScope, MirDebugScope> { - let mir = fcx.mir(); +pub fn create_mir_scopes(fcx: &FunctionContext, mir: &Mir, debug_context: &FunctionDebugContext) + -> IndexVec<VisibilityScope, MirDebugScope> { let null_scope = MirDebugScope { scope_metadata: ptr::null_mut(), file_start_pos: BytePos(0), @@ -53,8 +53,8 @@ pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec<VisibilityScope, Mir }; let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes); - let fn_metadata = match fcx.debug_context { - FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata, + let fn_metadata = match *debug_context { + FunctionDebugContext::RegularContext(ref data) => data.fn_metadata, FunctionDebugContext::DebugInfoDisabled | FunctionDebugContext::FunctionWithoutDebugInfo => { return scopes; diff --git a/src/librustc_trans/debuginfo/gdb.rs b/src/librustc_trans/debuginfo/gdb.rs index 8f937d3fe25..e8728a39993 100644 --- a/src/librustc_trans/debuginfo/gdb.rs +++ b/src/librustc_trans/debuginfo/gdb.rs @@ -13,37 +13,26 @@ use llvm; use common::{C_bytes, CrateContext, C_i32}; +use builder::Builder; use declare; use type_::Type; use session::config::NoDebugInfo; -use std::ffi::CString; use std::ptr; use syntax::attr; /// Inserts a side-effect free instruction sequence that makes sure that the /// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. -pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext) { +pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext, builder: &Builder) { if needs_gdb_debug_scripts_section(ccx) { - let empty = CString::new("").unwrap(); - let gdb_debug_scripts_section_global = - get_or_insert_gdb_debug_scripts_section_global(ccx); + let gdb_debug_scripts_section_global = get_or_insert_gdb_debug_scripts_section_global(ccx); + // Load just the first byte as that's all that's necessary to force + // LLVM to keep around the reference to the global. + let indices = [C_i32(ccx, 0), C_i32(ccx, 0)]; + let element = builder.inbounds_gep(gdb_debug_scripts_section_global, &indices); + let volative_load_instruction = builder.volatile_load(element); unsafe { - // Load just the first byte as that's all that's necessary to force - // LLVM to keep around the reference to the global. - let indices = [C_i32(ccx, 0), C_i32(ccx, 0)]; - let element = - llvm::LLVMBuildInBoundsGEP(ccx.raw_builder(), - gdb_debug_scripts_section_global, - indices.as_ptr(), - indices.len() as ::libc::c_uint, - empty.as_ptr()); - let volative_load_instruction = - llvm::LLVMBuildLoad(ccx.raw_builder(), - element, - empty.as_ptr()); - llvm::LLVMSetVolatile(volative_load_instruction, llvm::True); llvm::LLVMSetAlignment(volative_load_instruction, 1); } } diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index cda9fa46f17..511c9d3c13f 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -31,7 +31,7 @@ use rustc::ty::fold::TypeVisitor; use rustc::ty::subst::Substs; use rustc::ty::util::TypeIdHasher; use rustc::hir; -use rustc_data_structures::blake2b::Blake2bHasher; +use rustc_data_structures::ToHex; use {type_of, machine, monomorphize}; use common::CrateContext; use type_::Type; @@ -42,7 +42,6 @@ use util::common::path2cstr; use libc::{c_uint, c_longlong}; use std::ffi::CString; -use std::fmt::Write; use std::path::Path; use std::ptr; use syntax::ast; @@ -147,21 +146,11 @@ impl<'tcx> TypeMap<'tcx> { // The hasher we are using to generate the UniqueTypeId. We want // something that provides more than the 64 bits of the DefaultHasher. - const TYPE_ID_HASH_LENGTH: usize = 20; - let mut type_id_hasher = TypeIdHasher::new(cx.tcx(), - Blake2bHasher::new(TYPE_ID_HASH_LENGTH, &[])); + let mut type_id_hasher = TypeIdHasher::<[u8; 20]>::new(cx.tcx()); type_id_hasher.visit_ty(type_); - let mut hash_state = type_id_hasher.into_inner(); - let hash: &[u8] = hash_state.finalize(); - debug_assert!(hash.len() == TYPE_ID_HASH_LENGTH); - - let mut unique_type_id = String::with_capacity(TYPE_ID_HASH_LENGTH * 2); - - for byte in hash.into_iter() { - write!(&mut unique_type_id, "{:x}", byte).unwrap(); - } + let unique_type_id = type_id_hasher.finish().to_hex(); let key = self.unique_id_interner.intern(&unique_type_id); self.type_to_unique_id.insert(type_, UniqueTypeId(key)); @@ -881,25 +870,28 @@ impl<'tcx> MemberDescriptionFactory<'tcx> { // Creates MemberDescriptions for the fields of a struct struct StructMemberDescriptionFactory<'tcx> { + ty: Ty<'tcx>, variant: &'tcx ty::VariantDef, substs: &'tcx Substs<'tcx>, - is_simd: bool, span: Span, } impl<'tcx> StructMemberDescriptionFactory<'tcx> { fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Vec<MemberDescription> { - let field_size = if self.is_simd { - let fty = monomorphize::field_ty(cx.tcx(), - self.substs, - &self.variant.fields[0]); - Some(machine::llsize_of_alloc( - cx, - type_of::type_of(cx, fty) - ) as usize) - } else { - None + let layout = cx.layout_of(self.ty); + + let tmp; + let offsets = match *layout { + layout::Univariant { ref variant, .. } => &variant.offsets, + layout::Vector { element, count } => { + let element_size = element.size(&cx.tcx().data_layout).bytes(); + tmp = (0..count). + map(|i| layout::Size::from_bytes(i*element_size)) + .collect::<Vec<layout::Size>>(); + &tmp + } + _ => bug!("{} is not a struct", self.ty) }; self.variant.fields.iter().enumerate().map(|(i, f)| { @@ -910,11 +902,7 @@ impl<'tcx> StructMemberDescriptionFactory<'tcx> { }; let fty = monomorphize::field_ty(cx.tcx(), self.substs, f); - let offset = if self.is_simd { - FixedMemberOffset { bytes: i * field_size.unwrap() } - } else { - ComputedMemberOffset - }; + let offset = FixedMemberOffset { bytes: offsets[i].bytes() as usize}; MemberDescription { name: name, @@ -956,9 +944,9 @@ fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, struct_metadata_stub, struct_llvm_type, StructMDF(StructMemberDescriptionFactory { + ty: struct_type, variant: variant, substs: substs, - is_simd: struct_type.is_simd(), span: span, }) ) @@ -970,6 +958,7 @@ fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // Creates MemberDescriptions for the fields of a tuple struct TupleMemberDescriptionFactory<'tcx> { + ty: Ty<'tcx>, component_types: Vec<Ty<'tcx>>, span: Span, } @@ -977,6 +966,13 @@ struct TupleMemberDescriptionFactory<'tcx> { impl<'tcx> TupleMemberDescriptionFactory<'tcx> { fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Vec<MemberDescription> { + let layout = cx.layout_of(self.ty); + let offsets = if let layout::Univariant { ref variant, .. } = *layout { + &variant.offsets + } else { + bug!("{} is not a tuple", self.ty); + }; + self.component_types .iter() .enumerate() @@ -985,7 +981,7 @@ impl<'tcx> TupleMemberDescriptionFactory<'tcx> { name: format!("__{}", i), llvm_type: type_of::type_of(cx, component_type), type_metadata: type_metadata(cx, component_type, self.span), - offset: ComputedMemberOffset, + offset: FixedMemberOffset { bytes: offsets[i].bytes() as usize }, flags: DIFlags::FlagZero, } }).collect() @@ -1012,6 +1008,7 @@ fn prepare_tuple_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, NO_SCOPE_METADATA), tuple_llvm_type, TupleMDF(TupleMemberDescriptionFactory { + ty: tuple_type, component_types: component_types.to_vec(), span: span, }) @@ -1250,7 +1247,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { }, layout::StructWrappedNullablePointer { nonnull: ref struct_def, nndiscr, - ref discrfield, ..} => { + ref discrfield_source, ..} => { // Create a description of the non-null variant let (variant_type_metadata, variant_llvm_type, member_description_factory) = describe_enum_variant(cx, @@ -1273,12 +1270,12 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { // member's name. let null_variant_index = (1 - nndiscr) as usize; let null_variant_name = adt.variants[null_variant_index].name; - let discrfield = discrfield.iter() + let discrfield_source = discrfield_source.iter() .skip(1) .map(|x| x.to_string()) .collect::<Vec<_>>().join("$"); let union_member_name = format!("RUST$ENCODED$ENUM${}${}", - discrfield, + discrfield_source, null_variant_name); // Create the (singleton) list of descriptions of union members. @@ -1300,6 +1297,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { // Creates MemberDescriptions for the fields of a single enum variant. struct VariantMemberDescriptionFactory<'tcx> { + // Cloned from the layout::Struct describing the variant. + offsets: &'tcx [layout::Size], args: Vec<(String, Ty<'tcx>)>, discriminant_type_metadata: Option<DIType>, span: Span, @@ -1316,7 +1315,7 @@ impl<'tcx> VariantMemberDescriptionFactory<'tcx> { Some(metadata) if i == 0 => metadata, _ => type_metadata(cx, ty, self.span) }, - offset: ComputedMemberOffset, + offset: FixedMemberOffset { bytes: self.offsets[i].bytes() as usize }, flags: DIFlags::FlagZero } }).collect() @@ -1336,7 +1335,7 @@ enum EnumDiscriminantInfo { // full RecursiveTypeDescription. fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, enum_type: Ty<'tcx>, - struct_def: &layout::Struct, + struct_def: &'tcx layout::Struct, variant: &'tcx ty::VariantDef, discriminant_info: EnumDiscriminantInfo, containing_scope: DIScope, @@ -1420,6 +1419,7 @@ fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let member_description_factory = VariantMDF(VariantMemberDescriptionFactory { + offsets: &struct_def.offsets[..], args: args, discriminant_type_metadata: match discriminant_info { RegularDiscriminant(discriminant_type_metadata) => { @@ -1763,6 +1763,10 @@ pub fn create_global_var_metadata(cx: &CrateContext, let var_name = CString::new(var_name).unwrap(); let linkage_name = CString::new(linkage_name).unwrap(); + + let ty = cx.tcx().item_type(node_def_id); + let global_align = type_of::align_of(cx, ty); + unsafe { llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), var_scope, @@ -1773,7 +1777,9 @@ pub fn create_global_var_metadata(cx: &CrateContext, type_metadata, is_local_to_unit, global, - ptr::null_mut()); + ptr::null_mut(), + global_align as u64, + ); } } diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index aab70ab252a..86099d241df 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -27,7 +27,7 @@ use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use abi::Abi; -use common::{CrateContext, FunctionContext, Block, BlockAndBuilder}; +use common::{CrateContext, BlockAndBuilder}; use monomorphize::{self, Instance}; use rustc::ty::{self, Ty}; use rustc::mir; @@ -55,6 +55,7 @@ pub use self::create_scope_map::{create_mir_scopes, MirDebugScope}; pub use self::source_loc::start_emitting_source_locations; pub use self::metadata::create_global_var_metadata; pub use self::metadata::extend_scope_to_file; +pub use self::source_loc::set_source_location; #[allow(non_upper_case_globals)] const DW_TAG_auto_variable: c_uint = 0x100; @@ -65,7 +66,6 @@ const DW_TAG_arg_variable: c_uint = 0x101; pub struct CrateDebugContext<'tcx> { llcontext: ContextRef, builder: DIBuilderRef, - current_debug_location: Cell<InternalDebugLocation>, created_files: RefCell<FxHashMap<String, DIFile>>, created_enum_disr_types: RefCell<FxHashMap<(DefId, layout::Integer), DIType>>, @@ -83,40 +83,33 @@ impl<'tcx> CrateDebugContext<'tcx> { let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; // DIBuilder inherits context from the module, so we'd better use the same one let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; - return CrateDebugContext { + CrateDebugContext { llcontext: llcontext, builder: builder, - current_debug_location: Cell::new(InternalDebugLocation::UnknownLocation), created_files: RefCell::new(FxHashMap()), created_enum_disr_types: RefCell::new(FxHashMap()), type_map: RefCell::new(TypeMap::new()), namespace_map: RefCell::new(DefIdMap()), composite_types_completed: RefCell::new(FxHashSet()), - }; + } } } pub enum FunctionDebugContext { - RegularContext(Box<FunctionDebugContextData>), + RegularContext(FunctionDebugContextData), DebugInfoDisabled, FunctionWithoutDebugInfo, } impl FunctionDebugContext { - fn get_ref<'a>(&'a self, - span: Span) - -> &'a FunctionDebugContextData { + fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData { match *self { - FunctionDebugContext::RegularContext(box ref data) => data, + FunctionDebugContext::RegularContext(ref data) => data, FunctionDebugContext::DebugInfoDisabled => { - span_bug!(span, - "{}", - FunctionDebugContext::debuginfo_disabled_message()); + span_bug!(span, "{}", FunctionDebugContext::debuginfo_disabled_message()); } FunctionDebugContext::FunctionWithoutDebugInfo => { - span_bug!(span, - "{}", - FunctionDebugContext::should_be_ignored_message()); + span_bug!(span, "{}", FunctionDebugContext::should_be_ignored_message()); } } } @@ -134,7 +127,6 @@ impl FunctionDebugContext { pub struct FunctionDebugContextData { fn_metadata: DISubprogram, source_locations_enabled: Cell<bool>, - source_location_override: Cell<bool>, } pub enum VariableAccess<'a> { @@ -197,18 +189,6 @@ pub fn finalize(cx: &CrateContext) { }; } -/// Creates a function-specific debug context for a function w/o debuginfo. -pub fn empty_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>) - -> FunctionDebugContext { - if cx.sess().opts.debuginfo == NoDebugInfo { - return FunctionDebugContext::DebugInfoDisabled; - } - - // Clear the debug location so we don't assign them in the function prelude. - source_loc::set_debug_location(cx, None, UnknownLocation); - FunctionDebugContext::FunctionWithoutDebugInfo -} - /// Creates the function-specific debug context. /// /// Returns the FunctionDebugContext for the function which holds state needed @@ -225,15 +205,18 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, return FunctionDebugContext::DebugInfoDisabled; } - // Clear the debug location so we don't assign them in the function prelude. - // Do this here already, in case we do an early exit from this function. - source_loc::set_debug_location(cx, None, UnknownLocation); + for attr in cx.tcx().get_attrs(instance.def).iter() { + if attr.check_name("no_debug") { + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + } let containing_scope = get_containing_scope(cx, instance); let span = mir.span; // This can be the case for functions inlined from another crate if span == syntax_pos::DUMMY_SP { + // FIXME(simulacrum): Probably can't happen; remove. return FunctionDebugContext::FunctionWithoutDebugInfo; } @@ -293,10 +276,9 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, }; // Initialize fn debug context (including scope map and namespace map) - let fn_debug_context = box FunctionDebugContextData { + let fn_debug_context = FunctionDebugContextData { fn_metadata: fn_metadata, source_locations_enabled: Cell::new(false), - source_location_override: Cell::new(false), }; return FunctionDebugContext::RegularContext(fn_debug_context); @@ -441,14 +423,15 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } } -pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - variable_name: ast::Name, - variable_type: Ty<'tcx>, - scope_metadata: DIScope, - variable_access: VariableAccess, - variable_kind: VariableKind, - span: Span) { - let cx: &CrateContext = bcx.ccx(); +pub fn declare_local<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + dbg_context: &FunctionDebugContext, + variable_name: ast::Name, + variable_type: Ty<'tcx>, + scope_metadata: DIScope, + variable_access: VariableAccess, + variable_kind: VariableKind, + span: Span) { + let cx = bcx.ccx; let file = span_start(cx, span).file; let filename = file.name.clone(); @@ -462,6 +445,7 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, LocalVariable | CapturedVariable => (0, DW_TAG_auto_variable) }; + let align = ::type_of::align_of(cx, variable_type); let name = CString::new(variable_name.as_str().as_bytes()).unwrap(); match (variable_access, &[][..]) { @@ -478,12 +462,14 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, type_metadata, cx.sess().opts.optimize != config::OptLevel::No, DIFlags::FlagZero, - argument_index) + argument_index, + align as u64, + ) }; - source_loc::set_debug_location(cx, None, + source_loc::set_debug_location(bcx, InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); unsafe { - let debug_loc = llvm::LLVMGetCurrentDebugLocation(cx.raw_builder()); + let debug_loc = llvm::LLVMGetCurrentDebugLocation(bcx.llbuilder); let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( DIB(cx), alloca, @@ -491,38 +477,18 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, address_operations.as_ptr(), address_operations.len() as c_uint, debug_loc, - bcx.llbb); + bcx.llbb()); - llvm::LLVMSetInstDebugLocation(::build::B(bcx).llbuilder, instr); + llvm::LLVMSetInstDebugLocation(bcx.llbuilder, instr); } } } match variable_kind { ArgumentVariable(_) | CapturedVariable => { - assert!(!bcx.fcx - .debug_context - .get_ref(span) - .source_locations_enabled - .get()); - source_loc::set_debug_location(cx, None, UnknownLocation); + assert!(!dbg_context.get_ref(span).source_locations_enabled.get()); + source_loc::set_debug_location(bcx, UnknownLocation); } _ => { /* nothing to do */ } } } - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum DebugLoc { - ScopeAt(DIScope, Span), - None -} - -impl DebugLoc { - pub fn apply(self, fcx: &FunctionContext) { - source_loc::set_source_location(fcx, None, self); - } - - pub fn apply_to_bcx(self, bcx: &BlockAndBuilder) { - source_loc::set_source_location(bcx.fcx(), Some(bcx), self); - } -} diff --git a/src/librustc_trans/debuginfo/source_loc.rs b/src/librustc_trans/debuginfo/source_loc.rs index 1aee27c144a..e02c8be19a2 100644 --- a/src/librustc_trans/debuginfo/source_loc.rs +++ b/src/librustc_trans/debuginfo/source_loc.rs @@ -11,57 +11,40 @@ use self::InternalDebugLocation::*; use super::utils::{debug_context, span_start}; -use super::metadata::{UNKNOWN_COLUMN_NUMBER}; -use super::{FunctionDebugContext, DebugLoc}; +use super::metadata::UNKNOWN_COLUMN_NUMBER; +use super::FunctionDebugContext; use llvm; use llvm::debuginfo::DIScope; use builder::Builder; -use common::{CrateContext, FunctionContext}; use libc::c_uint; use std::ptr; -use syntax_pos::Pos; +use syntax_pos::{Span, Pos}; /// Sets the current debug location at the beginning of the span. /// /// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). -pub fn set_source_location(fcx: &FunctionContext, - builder: Option<&Builder>, - debug_loc: DebugLoc) { - let builder = builder.map(|b| b.llbuilder); - let function_debug_context = match fcx.debug_context { +pub fn set_source_location( + debug_context: &FunctionDebugContext, builder: &Builder, scope: DIScope, span: Span +) { + let function_debug_context = match *debug_context { FunctionDebugContext::DebugInfoDisabled => return, FunctionDebugContext::FunctionWithoutDebugInfo => { - set_debug_location(fcx.ccx, builder, UnknownLocation); + set_debug_location(builder, UnknownLocation); return; } - FunctionDebugContext::RegularContext(box ref data) => data + FunctionDebugContext::RegularContext(ref data) => data }; - if function_debug_context.source_location_override.get() { - // Just ignore any attempts to set a new debug location while - // the override is active. - return; - } - let dbg_loc = if function_debug_context.source_locations_enabled.get() { - let (scope, span) = match debug_loc { - DebugLoc::ScopeAt(scope, span) => (scope, span), - DebugLoc::None => { - set_debug_location(fcx.ccx, builder, UnknownLocation); - return; - } - }; - - debug!("set_source_location: {}", - fcx.ccx.sess().codemap().span_to_string(span)); - let loc = span_start(fcx.ccx, span); + debug!("set_source_location: {}", builder.ccx.sess().codemap().span_to_string(span)); + let loc = span_start(builder.ccx, span); InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) } else { UnknownLocation }; - set_debug_location(fcx.ccx, builder, dbg_loc); + set_debug_location(builder, dbg_loc); } /// Enables emitting source locations for the given functions. @@ -70,9 +53,9 @@ pub fn set_source_location(fcx: &FunctionContext, /// they are disabled when beginning to translate a new function. This functions /// switches source location emitting on and must therefore be called before the /// first real statement/expression of the function is translated. -pub fn start_emitting_source_locations(fcx: &FunctionContext) { - match fcx.debug_context { - FunctionDebugContext::RegularContext(box ref data) => { +pub fn start_emitting_source_locations(dbg_context: &FunctionDebugContext) { + match *dbg_context { + FunctionDebugContext::RegularContext(ref data) => { data.source_locations_enabled.set(true) }, _ => { /* safe to ignore */ } @@ -96,15 +79,7 @@ impl InternalDebugLocation { } } -pub fn set_debug_location(cx: &CrateContext, - builder: Option<llvm::BuilderRef>, - debug_location: InternalDebugLocation) { - if builder.is_none() { - if debug_location == debug_context(cx).current_debug_location.get() { - return; - } - } - +pub fn set_debug_location(builder: &Builder, debug_location: InternalDebugLocation) { let metadata_node = match debug_location { KnownLocation { scope, line, .. } => { // Always set the column to zero like Clang and GCC @@ -113,7 +88,7 @@ pub fn set_debug_location(cx: &CrateContext, unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation( - debug_context(cx).llcontext, + debug_context(builder.ccx).llcontext, line as c_uint, col as c_uint, scope, @@ -126,12 +101,7 @@ pub fn set_debug_location(cx: &CrateContext, } }; - if builder.is_none() { - debug_context(cx).current_debug_location.set(debug_location); - } - - let builder = builder.unwrap_or_else(|| cx.raw_builder()); unsafe { - llvm::LLVMSetCurrentDebugLocation(builder, metadata_node); + llvm::LLVMSetCurrentDebugLocation(builder.llbuilder, metadata_node); } } diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 90bc29c39e9..3989dae553f 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -13,19 +13,19 @@ // Code relating to drop glue. use std; +use std::iter; use llvm; use llvm::{ValueRef, get_param}; -use middle::lang_items::ExchangeFreeFnLangItem; +use middle::lang_items::BoxFreeFnLangItem; use rustc::ty::subst::{Substs}; use rustc::traits; -use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable}; -use adt; +use rustc::ty::{self, AdtKind, Ty, TypeFoldable}; +use rustc::ty::subst::Kind; +use adt::{self, MaybeSizedValue}; use base::*; -use build::*; -use callee::{Callee}; +use callee::Callee; use common::*; -use debuginfo::DebugLoc; use machine::*; use monomorphize; use trans_item::TransItem; @@ -34,69 +34,34 @@ use type_of::{type_of, sizing_type_of, align_of}; use type_::Type; use value::Value; use Disr; +use cleanup::CleanupScope; -use arena::TypedArena; use syntax_pos::DUMMY_SP; -pub fn trans_exchange_free_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - v: ValueRef, - size: ValueRef, - align: ValueRef, - debug_loc: DebugLoc) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("trans_exchange_free"); - - let def_id = langcall(bcx.tcx(), None, "", ExchangeFreeFnLangItem); - let args = [PointerCast(bcx, v, Type::i8p(bcx.ccx())), size, align]; - Callee::def(bcx.ccx(), def_id, bcx.tcx().intern_substs(&[])) - .call(bcx, debug_loc, &args, None).bcx -} +pub fn trans_exchange_free_ty<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + ptr: MaybeSizedValue, + content_ty: Ty<'tcx> +) { + let def_id = langcall(bcx.tcx(), None, "", BoxFreeFnLangItem); + let substs = bcx.tcx().mk_substs(iter::once(Kind::from(content_ty))); + let callee = Callee::def(bcx.ccx, def_id, substs); -pub fn trans_exchange_free<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - v: ValueRef, - size: u64, - align: u32, - debug_loc: DebugLoc) - -> Block<'blk, 'tcx> { - trans_exchange_free_dyn(cx, - v, - C_uint(cx.ccx(), size), - C_uint(cx.ccx(), align), - debug_loc) -} + let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); -pub fn trans_exchange_free_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - ptr: ValueRef, - content_ty: Ty<'tcx>, - debug_loc: DebugLoc) - -> Block<'blk, 'tcx> { - assert!(type_is_sized(bcx.ccx().tcx(), content_ty)); - let sizing_type = sizing_type_of(bcx.ccx(), content_ty); - let content_size = llsize_of_alloc(bcx.ccx(), sizing_type); - - // `Box<ZeroSizeType>` does not allocate. - if content_size != 0 { - let content_align = align_of(bcx.ccx(), content_ty); - trans_exchange_free(bcx, ptr, content_size, content_align, debug_loc) - } else { - bcx - } + let llret = bcx.call(callee.reify(bcx.ccx), + &[ptr.value, ptr.meta][..1 + ptr.has_meta() as usize], None); + fn_ty.apply_attrs_callsite(llret); } -pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>) -> bool { - tcx.type_needs_drop_given_env(ty, &tcx.empty_parameter_environment()) -} - -pub fn get_drop_glue_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - t: Ty<'tcx>) -> Ty<'tcx> { +pub fn get_drop_glue_type<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Ty<'tcx> { assert!(t.is_normalized_for_trans()); - let t = tcx.erase_regions(&t); + let t = scx.tcx().erase_regions(&t); // Even if there is no dtor for t, there might be one deeper down and we // might need to pass in the vtable ptr. - if !type_is_sized(tcx, t) { + if !scx.type_is_sized(t) { return t; } @@ -109,17 +74,16 @@ pub fn get_drop_glue_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // returned `tcx.types.i8` does not appear unsound. The impact on // code quality is unknown at this time.) - if !type_needs_drop(tcx, t) { - return tcx.types.i8; + if !scx.type_needs_drop(t) { + return scx.tcx().types.i8; } match t.sty { - ty::TyBox(typ) if !type_needs_drop(tcx, typ) - && type_is_sized(tcx, typ) => { - tcx.infer_ctxt(None, None, traits::Reveal::All).enter(|infcx| { + ty::TyBox(typ) if !scx.type_needs_drop(typ) && scx.type_is_sized(typ) => { + scx.tcx().infer_ctxt(None, None, traits::Reveal::All).enter(|infcx| { let layout = t.layout(&infcx).unwrap(); - if layout.size(&tcx.data_layout).bytes() == 0 { + if layout.size(&scx.tcx().data_layout).bytes() == 0 { // `Box<ZeroSizeType>` does not allocate. - tcx.types.i8 + scx.tcx().types.i8 } else { t } @@ -129,56 +93,36 @@ pub fn get_drop_glue_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - v: ValueRef, - t: Ty<'tcx>, - debug_loc: DebugLoc) -> Block<'blk, 'tcx> { - drop_ty_core(bcx, v, t, debug_loc, false) +fn drop_ty<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, args: MaybeSizedValue, t: Ty<'tcx>) { + call_drop_glue(bcx, args, t, false, None) } -pub fn drop_ty_core<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - v: ValueRef, - t: Ty<'tcx>, - debug_loc: DebugLoc, - skip_dtor: bool) - -> Block<'blk, 'tcx> { +pub fn call_drop_glue<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + mut args: MaybeSizedValue, + t: Ty<'tcx>, + skip_dtor: bool, + funclet: Option<&'a Funclet>, +) { // NB: v is an *alias* of type t here, not a direct value. - debug!("drop_ty_core(t={:?}, skip_dtor={})", t, skip_dtor); - let _icx = push_ctxt("drop_ty"); - if bcx.fcx.type_needs_drop(t) { - let ccx = bcx.ccx(); + debug!("call_drop_glue(t={:?}, skip_dtor={})", t, skip_dtor); + if bcx.ccx.shared().type_needs_drop(t) { + let ccx = bcx.ccx; let g = if skip_dtor { DropGlueKind::TyContents(t) } else { DropGlueKind::Ty(t) }; let glue = get_drop_glue_core(ccx, g); - let glue_type = get_drop_glue_type(ccx.tcx(), t); - let ptr = if glue_type != t { - PointerCast(bcx, v, type_of(ccx, glue_type).ptr_to()) - } else { - v - }; + let glue_type = get_drop_glue_type(ccx.shared(), t); + if glue_type != t { + args.value = bcx.pointercast(args.value, type_of(ccx, glue_type).ptr_to()); + } // No drop-hint ==> call standard drop glue - Call(bcx, glue, &[ptr], debug_loc); + bcx.call(glue, &[args.value, args.meta][..1 + args.has_meta() as usize], + funclet.map(|b| b.bundle())); } - bcx -} - -pub fn drop_ty_immediate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - v: ValueRef, - t: Ty<'tcx>, - debug_loc: DebugLoc, - skip_dtor: bool) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("drop_ty_immediate"); - let vp = alloc_ty(bcx, t, ""); - call_lifetime_start(bcx, vp); - store_ty(bcx, v, vp, t); - let bcx = drop_ty_core(bcx, vp, t, debug_loc, skip_dtor); - call_lifetime_end(bcx, vp); - bcx } pub fn get_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ValueRef { @@ -212,9 +156,8 @@ impl<'tcx> DropGlueKind<'tcx> { } } -fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - g: DropGlueKind<'tcx>) -> ValueRef { - let g = g.map_ty(|t| get_drop_glue_type(ccx.tcx(), t)); +fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKind<'tcx>) -> ValueRef { + let g = g.map_ty(|t| get_drop_glue_type(ccx.shared(), t)); match ccx.drop_glues().borrow().get(&g) { Some(&(glue, _)) => glue, None => { @@ -226,17 +169,12 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - g: DropGlueKind<'tcx>) { - let tcx = ccx.tcx(); - assert_eq!(g.ty(), get_drop_glue_type(tcx, g.ty())); - let (llfn, fn_ty) = ccx.drop_glues().borrow().get(&g).unwrap().clone(); - - let (arena, fcx): (TypedArena<_>, FunctionContext); - arena = TypedArena::new(); - fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &arena); +pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKind<'tcx>) { + assert_eq!(g.ty(), get_drop_glue_type(ccx.shared(), g.ty())); + let (llfn, _) = ccx.drop_glues().borrow().get(&g).unwrap().clone(); - let bcx = fcx.init(false); + let fcx = FunctionContext::new(ccx, llfn); + let mut bcx = fcx.get_entry_block(); ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1); // All glue functions take values passed *by alias*; this is a @@ -247,86 +185,127 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // llfn is expected be declared to take a parameter of the appropriate // type, so we don't need to explicitly cast the function parameter. - let bcx = make_drop_glue(bcx, get_param(llfn, 0), g); - fcx.finish(bcx, DebugLoc::None); -} - -fn trans_custom_dtor<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - t: Ty<'tcx>, - v0: ValueRef, - shallow_drop: bool) - -> Block<'blk, 'tcx> -{ - debug!("trans_custom_dtor t: {}", t); - let tcx = bcx.tcx(); - let mut bcx = bcx; - - let def = t.ty_adt_def().unwrap(); - - // Be sure to put the contents into a scope so we can use an invoke - // instruction to call the user destructor but still call the field - // destructors if the user destructor panics. - // - // FIXME (#14875) panic-in-drop semantics might be unsupported; we - // might well consider changing below to more direct code. - let contents_scope = bcx.fcx.push_custom_cleanup_scope(); - - // Issue #23611: schedule cleanup of contents, re-inspecting the - // discriminant (if any) in case of variant swap in drop code. - if !shallow_drop { - bcx.fcx.schedule_drop_adt_contents(contents_scope, v0, t); - } + // NB: v0 is an *alias* of type t here, not a direct value. + // Only drop the value when it ... well, we used to check for + // non-null, (and maybe we need to continue doing so), but we now + // must definitely check for special bit-patterns corresponding to + // the special dtor markings. + let t = g.ty(); - let (sized_args, unsized_args); - let args: &[ValueRef] = if type_is_sized(tcx, t) { - sized_args = [v0]; - &sized_args + let value = get_param(llfn, 0); + let ptr = if ccx.shared().type_is_sized(t) { + MaybeSizedValue::sized(value) } else { - // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments - unsized_args = [ - Load(bcx, get_dataptr(bcx, v0)), - Load(bcx, get_meta(bcx, v0)) - ]; - &unsized_args + MaybeSizedValue::unsized_(value, get_param(llfn, 1)) }; - let trait_ref = ty::Binder(ty::TraitRef { - def_id: tcx.lang_items.drop_trait().unwrap(), - substs: tcx.mk_substs_trait(t, &[]) - }); - let vtbl = match fulfill_obligation(bcx.ccx().shared(), DUMMY_SP, trait_ref) { - traits::VtableImpl(data) => data, - _ => bug!("dtor for {:?} is not an impl???", t) + let skip_dtor = match g { + DropGlueKind::Ty(_) => false, + DropGlueKind::TyContents(_) => true }; - let dtor_did = def.destructor().unwrap(); - bcx = Callee::def(bcx.ccx(), dtor_did, vtbl.substs) - .call(bcx, DebugLoc::None, args, None).bcx; - bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, contents_scope) + let bcx = match t.sty { + ty::TyBox(content_ty) => { + // Support for TyBox is built-in and its drop glue is + // special. It may move to library and have Drop impl. As + // a safe-guard, assert TyBox not used with TyContents. + assert!(!skip_dtor); + let ptr = if !bcx.ccx.shared().type_is_sized(content_ty) { + let llbox = bcx.load(get_dataptr(&bcx, ptr.value)); + let info = bcx.load(get_meta(&bcx, ptr.value)); + MaybeSizedValue::unsized_(llbox, info) + } else { + MaybeSizedValue::sized(bcx.load(ptr.value)) + }; + drop_ty(&bcx, ptr, content_ty); + trans_exchange_free_ty(&bcx, ptr, content_ty); + bcx + } + ty::TyDynamic(..) => { + // No support in vtable for distinguishing destroying with + // versus without calling Drop::drop. Assert caller is + // okay with always calling the Drop impl, if any. + assert!(!skip_dtor); + let dtor = bcx.load(ptr.meta); + bcx.call(dtor, &[ptr.value], None); + bcx + } + ty::TyAdt(def, ..) if def.dtor_kind().is_present() && !skip_dtor => { + let shallow_drop = def.is_union(); + let tcx = bcx.tcx(); + + let def = t.ty_adt_def().unwrap(); + + // Be sure to put the contents into a scope so we can use an invoke + // instruction to call the user destructor but still call the field + // destructors if the user destructor panics. + // + // FIXME (#14875) panic-in-drop semantics might be unsupported; we + // might well consider changing below to more direct code. + // Issue #23611: schedule cleanup of contents, re-inspecting the + // discriminant (if any) in case of variant swap in drop code. + let contents_scope = if !shallow_drop { + bcx.fcx().schedule_drop_adt_contents(ptr, t) + } else { + CleanupScope::noop() + }; + + let trait_ref = ty::Binder(ty::TraitRef { + def_id: tcx.lang_items.drop_trait().unwrap(), + substs: tcx.mk_substs_trait(t, &[]) + }); + let vtbl = match fulfill_obligation(bcx.ccx.shared(), DUMMY_SP, trait_ref) { + traits::VtableImpl(data) => data, + _ => bug!("dtor for {:?} is not an impl???", t) + }; + let dtor_did = def.destructor().unwrap(); + let callee = Callee::def(bcx.ccx, dtor_did, vtbl.substs); + let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); + let llret; + let args = &[ptr.value, ptr.meta][..1 + ptr.has_meta() as usize]; + if let Some(landing_pad) = contents_scope.landing_pad { + let normal_bcx = bcx.fcx().build_new_block("normal-return"); + llret = bcx.invoke(callee.reify(ccx), args, normal_bcx.llbb(), landing_pad, None); + bcx = normal_bcx; + } else { + llret = bcx.call(callee.reify(bcx.ccx), args, None); + } + fn_ty.apply_attrs_callsite(llret); + contents_scope.trans(&bcx); + bcx + } + ty::TyAdt(def, ..) if def.is_union() => { + bcx + } + _ => { + if bcx.ccx.shared().type_needs_drop(t) { + drop_structural_ty(bcx, ptr, t) + } else { + bcx + } + } + }; + bcx.ret_void(); } -pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, - t: Ty<'tcx>, info: ValueRef) - -> (ValueRef, ValueRef) { +pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + t: Ty<'tcx>, info: ValueRef) + -> (ValueRef, ValueRef) { debug!("calculate size of DST: {}; with lost info: {:?}", t, Value(info)); - if type_is_sized(bcx.tcx(), t) { - let sizing_type = sizing_type_of(bcx.ccx(), t); - let size = llsize_of_alloc(bcx.ccx(), sizing_type); - let align = align_of(bcx.ccx(), t); + if bcx.ccx.shared().type_is_sized(t) { + let sizing_type = sizing_type_of(bcx.ccx, t); + let size = llsize_of_alloc(bcx.ccx, sizing_type); + let align = align_of(bcx.ccx, t); debug!("size_and_align_of_dst t={} info={:?} size: {} align: {}", t, Value(info), size, align); - let size = C_uint(bcx.ccx(), size); - let align = C_uint(bcx.ccx(), align); + let size = C_uint(bcx.ccx, size); + let align = C_uint(bcx.ccx, align); return (size, align); } - if bcx.is_unreachable() { - let llty = Type::int(bcx.ccx()); - return (C_undef(llty), C_undef(llty)); - } match t.sty { ty::TyAdt(def, substs) => { - let ccx = bcx.ccx(); + let ccx = bcx.ccx; // First get the size of all statically known fields. // Don't use type_of::sizing_type_of because that expects t to be sized, // and it also rounds up to alignment, which we want to avoid, @@ -389,7 +368,7 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, // // `(size + (align-1)) & -align` - let addend = bcx.sub(align, C_uint(bcx.ccx(), 1_u64)); + let addend = bcx.sub(align, C_uint(bcx.ccx, 1_u64)); let size = bcx.and(bcx.add(size, addend), bcx.neg(align)); (size, align) @@ -397,7 +376,7 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, ty::TyDynamic(..) => { // info points to the vtable and the second entry in the vtable is the // dynamic size of the object. - let info = bcx.pointercast(info, Type::int(bcx.ccx()).ptr_to()); + let info = bcx.pointercast(info, Type::int(bcx.ccx).ptr_to()); let size_ptr = bcx.gepi(info, &[1]); let align_ptr = bcx.gepi(info, &[2]); (bcx.load(size_ptr), bcx.load(align_ptr)) @@ -406,194 +385,92 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, let unit_ty = t.sequence_element_type(bcx.tcx()); // The info in this case is the length of the str, so the size is that // times the unit size. - let llunit_ty = sizing_type_of(bcx.ccx(), unit_ty); - let unit_align = llalign_of_min(bcx.ccx(), llunit_ty); - let unit_size = llsize_of_alloc(bcx.ccx(), llunit_ty); - (bcx.mul(info, C_uint(bcx.ccx(), unit_size)), - C_uint(bcx.ccx(), unit_align)) + let llunit_ty = sizing_type_of(bcx.ccx, unit_ty); + let unit_align = llalign_of_min(bcx.ccx, llunit_ty); + let unit_size = llsize_of_alloc(bcx.ccx, llunit_ty); + (bcx.mul(info, C_uint(bcx.ccx, unit_size)), + C_uint(bcx.ccx, unit_align)) } _ => bug!("Unexpected unsized type, found {}", t) } } -fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - v0: ValueRef, - g: DropGlueKind<'tcx>) - -> Block<'blk, 'tcx> { - let t = g.ty(); - - let skip_dtor = match g { DropGlueKind::Ty(_) => false, DropGlueKind::TyContents(_) => true }; - // NB: v0 is an *alias* of type t here, not a direct value. - let _icx = push_ctxt("make_drop_glue"); - - // Only drop the value when it ... well, we used to check for - // non-null, (and maybe we need to continue doing so), but we now - // must definitely check for special bit-patterns corresponding to - // the special dtor markings. - - match t.sty { - ty::TyBox(content_ty) => { - // Support for TyBox is built-in and its drop glue is - // special. It may move to library and have Drop impl. As - // a safe-guard, assert TyBox not used with TyContents. - assert!(!skip_dtor); - if !type_is_sized(bcx.tcx(), content_ty) { - let llval = get_dataptr(bcx, v0); - let llbox = Load(bcx, llval); - let bcx = drop_ty(bcx, v0, content_ty, DebugLoc::None); - // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments - let info = get_meta(bcx, v0); - let info = Load(bcx, info); - let (llsize, llalign) = - size_and_align_of_dst(&bcx.build(), content_ty, info); - - // `Box<ZeroSizeType>` does not allocate. - let needs_free = ICmp(bcx, - llvm::IntNE, - llsize, - C_uint(bcx.ccx(), 0u64), - DebugLoc::None); - with_cond(bcx, needs_free, |bcx| { - trans_exchange_free_dyn(bcx, llbox, llsize, llalign, DebugLoc::None) - }) - } else { - let llval = v0; - let llbox = Load(bcx, llval); - let bcx = drop_ty(bcx, llbox, content_ty, DebugLoc::None); - trans_exchange_free_ty(bcx, llbox, content_ty, DebugLoc::None) - } - } - ty::TyDynamic(..) => { - // No support in vtable for distinguishing destroying with - // versus without calling Drop::drop. Assert caller is - // okay with always calling the Drop impl, if any. - // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments - assert!(!skip_dtor); - let data_ptr = get_dataptr(bcx, v0); - let vtable_ptr = Load(bcx, get_meta(bcx, v0)); - let dtor = Load(bcx, vtable_ptr); - Call(bcx, - dtor, - &[PointerCast(bcx, Load(bcx, data_ptr), Type::i8p(bcx.ccx()))], - DebugLoc::None); - bcx - } - ty::TyAdt(def, ..) if def.dtor_kind().is_present() && !skip_dtor => { - trans_custom_dtor(bcx, t, v0, def.is_union()) - } - ty::TyAdt(def, ..) if def.is_union() => { - bcx - } - _ => { - if bcx.fcx.type_needs_drop(t) { - drop_structural_ty(bcx, v0, t) - } else { - bcx - } - } - } -} - // Iterates through the elements of a structural type, dropping them. -fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - av: ValueRef, - t: Ty<'tcx>) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("drop_structural_ty"); - - fn iter_variant<'blk, 'tcx>(cx: Block<'blk, 'tcx>, - t: Ty<'tcx>, - av: adt::MaybeSizedValue, - variant: &'tcx ty::VariantDef, - substs: &Substs<'tcx>) - -> Block<'blk, 'tcx> { - let _icx = push_ctxt("iter_variant"); +fn drop_structural_ty<'a, 'tcx>(cx: BlockAndBuilder<'a, 'tcx>, + ptr: MaybeSizedValue, + t: Ty<'tcx>) + -> BlockAndBuilder<'a, 'tcx> { + fn iter_variant<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>, + t: Ty<'tcx>, + av: adt::MaybeSizedValue, + variant: &'tcx ty::VariantDef, + substs: &Substs<'tcx>) { let tcx = cx.tcx(); - let mut cx = cx; - for (i, field) in variant.fields.iter().enumerate() { let arg = monomorphize::field_ty(tcx, substs, field); - cx = drop_ty(cx, - adt::trans_field_ptr(cx, t, av, Disr::from(variant.disr_val), i), - arg, DebugLoc::None); + let field_ptr = adt::trans_field_ptr(&cx, t, av, Disr::from(variant.disr_val), i); + drop_ty(&cx, MaybeSizedValue::sized(field_ptr), arg); } - return cx; } - let value = if type_is_sized(cx.tcx(), t) { - adt::MaybeSizedValue::sized(av) - } else { - // FIXME(#36457) -- we should pass unsized values as two arguments - let data = Load(cx, get_dataptr(cx, av)); - let info = Load(cx, get_meta(cx, av)); - adt::MaybeSizedValue::unsized_(data, info) - }; - let mut cx = cx; match t.sty { ty::TyClosure(def_id, substs) => { for (i, upvar_ty) in substs.upvar_tys(def_id, cx.tcx()).enumerate() { - let llupvar = adt::trans_field_ptr(cx, t, value, Disr(0), i); - cx = drop_ty(cx, llupvar, upvar_ty, DebugLoc::None); + let llupvar = adt::trans_field_ptr(&cx, t, ptr, Disr(0), i); + drop_ty(&cx, MaybeSizedValue::sized(llupvar), upvar_ty); } } ty::TyArray(_, n) => { - let base = get_dataptr(cx, value.value); - let len = C_uint(cx.ccx(), n); + let base = get_dataptr(&cx, ptr.value); + let len = C_uint(cx.ccx, n); let unit_ty = t.sequence_element_type(cx.tcx()); - cx = tvec::slice_for_each(cx, base, unit_ty, len, - |bb, vv| drop_ty(bb, vv, unit_ty, DebugLoc::None)); + cx = tvec::slice_for_each(&cx, base, unit_ty, len, + |bb, vv| drop_ty(bb, MaybeSizedValue::sized(vv), unit_ty)); } ty::TySlice(_) | ty::TyStr => { let unit_ty = t.sequence_element_type(cx.tcx()); - cx = tvec::slice_for_each(cx, value.value, unit_ty, value.meta, - |bb, vv| drop_ty(bb, vv, unit_ty, DebugLoc::None)); + cx = tvec::slice_for_each(&cx, ptr.value, unit_ty, ptr.meta, + |bb, vv| drop_ty(bb, MaybeSizedValue::sized(vv), unit_ty)); } ty::TyTuple(ref args) => { for (i, arg) in args.iter().enumerate() { - let llfld_a = adt::trans_field_ptr(cx, t, value, Disr(0), i); - cx = drop_ty(cx, llfld_a, *arg, DebugLoc::None); + let llfld_a = adt::trans_field_ptr(&cx, t, ptr, Disr(0), i); + drop_ty(&cx, MaybeSizedValue::sized(llfld_a), *arg); } } ty::TyAdt(adt, substs) => match adt.adt_kind() { AdtKind::Struct => { let VariantInfo { fields, discr } = VariantInfo::from_ty(cx.tcx(), t, None); for (i, &Field(_, field_ty)) in fields.iter().enumerate() { - let llfld_a = adt::trans_field_ptr(cx, t, value, Disr::from(discr), i); - - let val = if type_is_sized(cx.tcx(), field_ty) { - llfld_a + let llfld_a = adt::trans_field_ptr(&cx, t, ptr, Disr::from(discr), i); + let ptr = if cx.ccx.shared().type_is_sized(field_ty) { + MaybeSizedValue::sized(llfld_a) } else { - // FIXME(#36457) -- we should pass unsized values as two arguments - let scratch = alloc_ty(cx, field_ty, "__fat_ptr_iter"); - Store(cx, llfld_a, get_dataptr(cx, scratch)); - Store(cx, value.meta, get_meta(cx, scratch)); - scratch + MaybeSizedValue::unsized_(llfld_a, ptr.meta) }; - cx = drop_ty(cx, val, field_ty, DebugLoc::None); + drop_ty(&cx, ptr, field_ty); } } AdtKind::Union => { bug!("Union in `glue::drop_structural_ty`"); } AdtKind::Enum => { - let fcx = cx.fcx; - let ccx = fcx.ccx; let n_variants = adt.variants.len(); // NB: we must hit the discriminant first so that structural // comparison know not to proceed when the discriminants differ. - match adt::trans_switch(cx, t, av, false) { + match adt::trans_switch(&cx, t, ptr.value, false) { (adt::BranchKind::Single, None) => { if n_variants != 0 { assert!(n_variants == 1); - cx = iter_variant(cx, t, adt::MaybeSizedValue::sized(av), - &adt.variants[0], substs); + iter_variant(&cx, t, ptr, &adt.variants[0], substs); } } (adt::BranchKind::Switch, Some(lldiscrim_a)) => { - cx = drop_ty(cx, lldiscrim_a, cx.tcx().types.isize, DebugLoc::None); + let tcx = cx.tcx(); + drop_ty(&cx, MaybeSizedValue::sized(lldiscrim_a), tcx.types.isize); // Create a fall-through basic block for the "else" case of // the switch instruction we're about to generate. Note that @@ -608,27 +485,23 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, // from the outer function, and any other use case will only // call this for an already-valid enum in which case the `ret // void` will never be hit. - let ret_void_cx = fcx.new_block("enum-iter-ret-void"); - RetVoid(ret_void_cx, DebugLoc::None); - let llswitch = Switch(cx, lldiscrim_a, ret_void_cx.llbb, n_variants); - let next_cx = fcx.new_block("enum-iter-next"); + let ret_void_cx = cx.fcx().build_new_block("enum-iter-ret-void"); + ret_void_cx.ret_void(); + let llswitch = cx.switch(lldiscrim_a, ret_void_cx.llbb(), n_variants); + let next_cx = cx.fcx().build_new_block("enum-iter-next"); for variant in &adt.variants { - let variant_cx = fcx.new_block(&format!("enum-iter-variant-{}", - &variant.disr_val - .to_string())); - let case_val = adt::trans_case(cx, t, Disr::from(variant.disr_val)); - AddCase(llswitch, case_val, variant_cx.llbb); - let variant_cx = iter_variant(variant_cx, - t, - value, - variant, - substs); - Br(variant_cx, next_cx.llbb, DebugLoc::None); + let variant_cx_name = format!("enum-iter-variant-{}", + &variant.disr_val.to_string()); + let variant_cx = cx.fcx().build_new_block(&variant_cx_name); + let case_val = adt::trans_case(&cx, t, Disr::from(variant.disr_val)); + variant_cx.add_case(llswitch, case_val, variant_cx.llbb()); + iter_variant(&variant_cx, t, ptr, variant, substs); + variant_cx.br(next_cx.llbb()); } cx = next_cx; } - _ => ccx.sess().unimpl("value from adt::trans_switch in drop_structural_ty"), + _ => cx.ccx.sess().unimpl("value from adt::trans_switch in drop_structural_ty"), } } }, diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 577ffbad134..b7116ba1f33 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -10,7 +10,6 @@ #![allow(non_upper_case_globals)] -use arena::TypedArena; use intrinsics::{self, Intrinsic}; use libc; use llvm; @@ -18,9 +17,7 @@ use llvm::{ValueRef}; use abi::{Abi, FnType}; use adt; use base::*; -use build::*; use common::*; -use debuginfo::DebugLoc; use declare; use glue; use type_of; @@ -33,7 +30,7 @@ use syntax::ast; use syntax::symbol::Symbol; use rustc::session::Session; -use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::Span; use std::cmp::Ordering; use std::iter; @@ -79,6 +76,7 @@ fn get_simple_intrinsic(ccx: &CrateContext, name: &str) -> Option<ValueRef> { "roundf32" => "llvm.round.f32", "roundf64" => "llvm.round.f64", "assume" => "llvm.assume", + "abort" => "llvm.trap", _ => return None }; Some(ccx.get_intrinsic(&llvm_name)) @@ -87,19 +85,15 @@ fn get_simple_intrinsic(ccx: &CrateContext, name: &str) -> Option<ValueRef> { /// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs, /// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics, /// add them to librustc_trans/trans/context.rs -pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, - callee_ty: Ty<'tcx>, - fn_ty: &FnType, - llargs: &[ValueRef], - llresult: ValueRef, - call_debug_location: DebugLoc) - -> Result<'blk, 'tcx> { - let fcx = bcx.fcx; - let ccx = fcx.ccx; +pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + callee_ty: Ty<'tcx>, + fn_ty: &FnType, + llargs: &[ValueRef], + llresult: ValueRef, + span: Span) { + let ccx = bcx.ccx; let tcx = bcx.tcx(); - let _icx = push_ctxt("trans_intrinsic_call"); - let (def_id, substs, fty) = match callee_ty.sty { ty::TyFnDef(def_id, substs, ref fty) => (def_id, substs, fty), _ => bug!("expected fn item type, found {}", callee_ty) @@ -110,223 +104,150 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let ret_ty = sig.output(); let name = &*tcx.item_name(def_id).as_str(); - let span = match call_debug_location { - DebugLoc::ScopeAt(_, span) => span, - DebugLoc::None => { - span_bug!(fcx.span.unwrap_or(DUMMY_SP), - "intrinsic `{}` called with missing span", name); - } - }; - - // These are the only intrinsic functions that diverge. - if name == "abort" { - let llfn = ccx.get_intrinsic(&("llvm.trap")); - Call(bcx, llfn, &[], call_debug_location); - Unreachable(bcx); - return Result::new(bcx, C_undef(Type::nil(ccx).ptr_to())); - } else if name == "unreachable" { - Unreachable(bcx); - return Result::new(bcx, C_nil(ccx)); - } - let llret_ty = type_of::type_of(ccx, ret_ty); let simple = get_simple_intrinsic(ccx, name); - let llval = match (simple, name) { - (Some(llfn), _) => { - Call(bcx, llfn, &llargs, call_debug_location) + let llval = match name { + _ if simple.is_some() => { + bcx.call(simple.unwrap(), &llargs, None) } - (_, "likely") => { + "unreachable" => { + return; + }, + "likely" => { let expect = ccx.get_intrinsic(&("llvm.expect.i1")); - Call(bcx, expect, &[llargs[0], C_bool(ccx, true)], call_debug_location) + bcx.call(expect, &[llargs[0], C_bool(ccx, true)], None) } - (_, "unlikely") => { + "unlikely" => { let expect = ccx.get_intrinsic(&("llvm.expect.i1")); - Call(bcx, expect, &[llargs[0], C_bool(ccx, false)], call_debug_location) + bcx.call(expect, &[llargs[0], C_bool(ccx, false)], None) } - (_, "try") => { - bcx = try_intrinsic(bcx, llargs[0], llargs[1], llargs[2], llresult, - call_debug_location); + "try" => { + try_intrinsic(bcx, llargs[0], llargs[1], llargs[2], llresult); C_nil(ccx) } - (_, "breakpoint") => { + "breakpoint" => { let llfn = ccx.get_intrinsic(&("llvm.debugtrap")); - Call(bcx, llfn, &[], call_debug_location) + bcx.call(llfn, &[], None) } - (_, "size_of") => { + "size_of" => { let tp_ty = substs.type_at(0); let lltp_ty = type_of::type_of(ccx, tp_ty); C_uint(ccx, machine::llsize_of_alloc(ccx, lltp_ty)) } - (_, "size_of_val") => { + "size_of_val" => { let tp_ty = substs.type_at(0); - if !type_is_sized(tcx, tp_ty) { + if !bcx.ccx.shared().type_is_sized(tp_ty) { let (llsize, _) = - glue::size_and_align_of_dst(&bcx.build(), tp_ty, llargs[1]); + glue::size_and_align_of_dst(bcx, tp_ty, llargs[1]); llsize } else { let lltp_ty = type_of::type_of(ccx, tp_ty); C_uint(ccx, machine::llsize_of_alloc(ccx, lltp_ty)) } } - (_, "min_align_of") => { + "min_align_of" => { let tp_ty = substs.type_at(0); C_uint(ccx, type_of::align_of(ccx, tp_ty)) } - (_, "min_align_of_val") => { + "min_align_of_val" => { let tp_ty = substs.type_at(0); - if !type_is_sized(tcx, tp_ty) { + if !bcx.ccx.shared().type_is_sized(tp_ty) { let (_, llalign) = - glue::size_and_align_of_dst(&bcx.build(), tp_ty, llargs[1]); + glue::size_and_align_of_dst(bcx, tp_ty, llargs[1]); llalign } else { C_uint(ccx, type_of::align_of(ccx, tp_ty)) } } - (_, "pref_align_of") => { + "pref_align_of" => { let tp_ty = substs.type_at(0); let lltp_ty = type_of::type_of(ccx, tp_ty); C_uint(ccx, machine::llalign_of_pref(ccx, lltp_ty)) } - (_, "drop_in_place") => { - let tp_ty = substs.type_at(0); - let is_sized = type_is_sized(tcx, tp_ty); - let ptr = if is_sized { - llargs[0] - } else { - // FIXME(#36457) -- we should pass unsized values as two arguments - let scratch = alloc_ty(bcx, tp_ty, "drop"); - call_lifetime_start(bcx, scratch); - Store(bcx, llargs[0], get_dataptr(bcx, scratch)); - Store(bcx, llargs[1], get_meta(bcx, scratch)); - scratch - }; - glue::drop_ty(bcx, ptr, tp_ty, call_debug_location); - if !is_sized { - call_lifetime_end(bcx, ptr); - } - C_nil(ccx) - } - (_, "type_name") => { + "type_name" => { let tp_ty = substs.type_at(0); let ty_name = Symbol::intern(&tp_ty.to_string()).as_str(); C_str_slice(ccx, ty_name) } - (_, "type_id") => { + "type_id" => { C_u64(ccx, ccx.tcx().type_id_hash(substs.type_at(0))) } - (_, "init") => { - let tp_ty = substs.type_at(0); - if !type_is_zero_size(ccx, tp_ty) { - // Just zero out the stack slot. (See comment on base::memzero for explanation) - init_zero_mem(bcx, llresult, tp_ty); + "init" => { + let ty = substs.type_at(0); + if !type_is_zero_size(ccx, ty) { + // Just zero out the stack slot. + // If we store a zero constant, LLVM will drown in vreg allocation for large data + // structures, and the generated code will be awful. (A telltale sign of this is + // large quantities of `mov [byte ptr foo],0` in the generated code.) + memset_intrinsic(bcx, false, ty, llresult, C_u8(ccx, 0), C_uint(ccx, 1usize)); } C_nil(ccx) } // Effectively no-ops - (_, "uninit") | (_, "forget") => { + "uninit" | "forget" => { C_nil(ccx) } - (_, "needs_drop") => { + "needs_drop" => { let tp_ty = substs.type_at(0); - C_bool(ccx, bcx.fcx.type_needs_drop(tp_ty)) + C_bool(ccx, bcx.ccx.shared().type_needs_drop(tp_ty)) } - (_, "offset") => { + "offset" => { let ptr = llargs[0]; let offset = llargs[1]; - InBoundsGEP(bcx, ptr, &[offset]) + bcx.inbounds_gep(ptr, &[offset]) } - (_, "arith_offset") => { + "arith_offset" => { let ptr = llargs[0]; let offset = llargs[1]; - GEP(bcx, ptr, &[offset]) + bcx.gep(ptr, &[offset]) } - (_, "copy_nonoverlapping") => { - copy_intrinsic(bcx, - false, - false, - substs.type_at(0), - llargs[1], - llargs[0], - llargs[2], - call_debug_location) + "copy_nonoverlapping" => { + copy_intrinsic(bcx, false, false, substs.type_at(0), llargs[1], llargs[0], llargs[2]) } - (_, "copy") => { - copy_intrinsic(bcx, - true, - false, - substs.type_at(0), - llargs[1], - llargs[0], - llargs[2], - call_debug_location) + "copy" => { + copy_intrinsic(bcx, true, false, substs.type_at(0), llargs[1], llargs[0], llargs[2]) } - (_, "write_bytes") => { - memset_intrinsic(bcx, - false, - substs.type_at(0), - llargs[0], - llargs[1], - llargs[2], - call_debug_location) + "write_bytes" => { + memset_intrinsic(bcx, false, substs.type_at(0), llargs[0], llargs[1], llargs[2]) } - (_, "volatile_copy_nonoverlapping_memory") => { - copy_intrinsic(bcx, - false, - true, - substs.type_at(0), - llargs[0], - llargs[1], - llargs[2], - call_debug_location) + "volatile_copy_nonoverlapping_memory" => { + copy_intrinsic(bcx, false, true, substs.type_at(0), llargs[0], llargs[1], llargs[2]) } - (_, "volatile_copy_memory") => { - copy_intrinsic(bcx, - true, - true, - substs.type_at(0), - llargs[0], - llargs[1], - llargs[2], - call_debug_location) + "volatile_copy_memory" => { + copy_intrinsic(bcx, true, true, substs.type_at(0), llargs[0], llargs[1], llargs[2]) } - (_, "volatile_set_memory") => { - memset_intrinsic(bcx, - true, - substs.type_at(0), - llargs[0], - llargs[1], - llargs[2], - call_debug_location) + "volatile_set_memory" => { + memset_intrinsic(bcx, true, substs.type_at(0), llargs[0], llargs[1], llargs[2]) } - (_, "volatile_load") => { + "volatile_load" => { let tp_ty = substs.type_at(0); let mut ptr = llargs[0]; if let Some(ty) = fn_ty.ret.cast { - ptr = PointerCast(bcx, ptr, ty.ptr_to()); + ptr = bcx.pointercast(ptr, ty.ptr_to()); } - let load = VolatileLoad(bcx, ptr); + let load = bcx.volatile_load(ptr); unsafe { llvm::LLVMSetAlignment(load, type_of::align_of(ccx, tp_ty)); } to_immediate(bcx, load, tp_ty) }, - (_, "volatile_store") => { + "volatile_store" => { let tp_ty = substs.type_at(0); - if type_is_fat_ptr(bcx.tcx(), tp_ty) { - VolatileStore(bcx, llargs[1], get_dataptr(bcx, llargs[0])); - VolatileStore(bcx, llargs[2], get_meta(bcx, llargs[0])); + if type_is_fat_ptr(bcx.ccx, tp_ty) { + bcx.volatile_store(llargs[1], get_dataptr(bcx, llargs[0])); + bcx.volatile_store(llargs[2], get_meta(bcx, llargs[0])); } else { let val = if fn_ty.args[1].is_indirect() { - Load(bcx, llargs[1]) + bcx.load(llargs[1]) } else { from_immediate(bcx, llargs[1]) }; - let ptr = PointerCast(bcx, llargs[0], val_ty(val).ptr_to()); - let store = VolatileStore(bcx, val, ptr); + let ptr = bcx.pointercast(llargs[0], val_ty(val).ptr_to()); + let store = bcx.volatile_store(val, ptr); unsafe { llvm::LLVMSetAlignment(store, type_of::align_of(ccx, tp_ty)); } @@ -334,49 +255,58 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, C_nil(ccx) }, - (_, "ctlz") | (_, "cttz") | (_, "ctpop") | (_, "bswap") | - (_, "add_with_overflow") | (_, "sub_with_overflow") | (_, "mul_with_overflow") | - (_, "overflowing_add") | (_, "overflowing_sub") | (_, "overflowing_mul") | - (_, "unchecked_div") | (_, "unchecked_rem") => { + "ctlz" | "cttz" | "ctpop" | "bswap" | + "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" | + "overflowing_add" | "overflowing_sub" | "overflowing_mul" | + "unchecked_div" | "unchecked_rem" => { let sty = &arg_tys[0].sty; match int_type_width_signed(sty, ccx) { Some((width, signed)) => match name { - "ctlz" => count_zeros_intrinsic(bcx, &format!("llvm.ctlz.i{}", width), - llargs[0], call_debug_location), - "cttz" => count_zeros_intrinsic(bcx, &format!("llvm.cttz.i{}", width), - llargs[0], call_debug_location), - "ctpop" => Call(bcx, ccx.get_intrinsic(&format!("llvm.ctpop.i{}", width)), - &llargs, call_debug_location), + "ctlz" | "cttz" => { + let y = C_bool(bcx.ccx, false); + let llfn = ccx.get_intrinsic(&format!("llvm.{}.i{}", name, width)); + bcx.call(llfn, &[llargs[0], y], None) + } + "ctpop" => bcx.call(ccx.get_intrinsic(&format!("llvm.ctpop.i{}", width)), + &llargs, None), "bswap" => { if width == 8 { llargs[0] // byte swap a u8/i8 is just a no-op } else { - Call(bcx, ccx.get_intrinsic(&format!("llvm.bswap.i{}", width)), - &llargs, call_debug_location) + bcx.call(ccx.get_intrinsic(&format!("llvm.bswap.i{}", width)), + &llargs, None) } } "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => { let intrinsic = format!("llvm.{}{}.with.overflow.i{}", if signed { 's' } else { 'u' }, &name[..3], width); - with_overflow_intrinsic(bcx, &intrinsic, llargs[0], llargs[1], llresult, - call_debug_location) + let llfn = bcx.ccx.get_intrinsic(&intrinsic); + + // Convert `i1` to a `bool`, and write it to the out parameter + let val = bcx.call(llfn, &[llargs[0], llargs[1]], None); + let result = bcx.extract_value(val, 0); + let overflow = bcx.zext(bcx.extract_value(val, 1), Type::bool(ccx)); + bcx.store(result, bcx.struct_gep(llresult, 0)); + bcx.store(overflow, bcx.struct_gep(llresult, 1)); + + C_nil(bcx.ccx) }, - "overflowing_add" => Add(bcx, llargs[0], llargs[1], call_debug_location), - "overflowing_sub" => Sub(bcx, llargs[0], llargs[1], call_debug_location), - "overflowing_mul" => Mul(bcx, llargs[0], llargs[1], call_debug_location), + "overflowing_add" => bcx.add(llargs[0], llargs[1]), + "overflowing_sub" => bcx.sub(llargs[0], llargs[1]), + "overflowing_mul" => bcx.mul(llargs[0], llargs[1]), "unchecked_div" => if signed { - SDiv(bcx, llargs[0], llargs[1], call_debug_location) + bcx.sdiv(llargs[0], llargs[1]) } else { - UDiv(bcx, llargs[0], llargs[1], call_debug_location) + bcx.udiv(llargs[0], llargs[1]) }, "unchecked_rem" => if signed { - SRem(bcx, llargs[0], llargs[1], call_debug_location) + bcx.srem(llargs[0], llargs[1]) } else { - URem(bcx, llargs[0], llargs[1], call_debug_location) + bcx.urem(llargs[0], llargs[1]) }, _ => bug!(), }, @@ -390,17 +320,16 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } }, - (_, "fadd_fast") | (_, "fsub_fast") | (_, "fmul_fast") | (_, "fdiv_fast") | - (_, "frem_fast") => { + "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { let sty = &arg_tys[0].sty; match float_type_width(sty) { Some(_width) => match name { - "fadd_fast" => FAddFast(bcx, llargs[0], llargs[1], call_debug_location), - "fsub_fast" => FSubFast(bcx, llargs[0], llargs[1], call_debug_location), - "fmul_fast" => FMulFast(bcx, llargs[0], llargs[1], call_debug_location), - "fdiv_fast" => FDivFast(bcx, llargs[0], llargs[1], call_debug_location), - "frem_fast" => FRemFast(bcx, llargs[0], llargs[1], call_debug_location), + "fadd_fast" => bcx.fadd_fast(llargs[0], llargs[1]), + "fsub_fast" => bcx.fsub_fast(llargs[0], llargs[1]), + "fmul_fast" => bcx.fmul_fast(llargs[0], llargs[1]), + "fdiv_fast" => bcx.fdiv_fast(llargs[0], llargs[1]), + "frem_fast" => bcx.frem_fast(llargs[0], llargs[1]), _ => bug!(), }, None => { @@ -414,7 +343,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, }, - (_, "discriminant_value") => { + "discriminant_value" => { let val_ty = substs.type_at(0); match val_ty.sty { ty::TyAdt(adt, ..) if adt.is_enum() => { @@ -424,17 +353,16 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, _ => C_null(llret_ty) } } - (_, name) if name.starts_with("simd_") => { + name if name.starts_with("simd_") => { generic_simd_intrinsic(bcx, name, callee_ty, &llargs, ret_ty, llret_ty, - call_debug_location, span) } // This requires that atomic intrinsics follow a specific naming pattern: // "atomic_<operation>[_<ordering>]", and no ordering means SeqCst - (_, name) if name.starts_with("atomic_") => { + name if name.starts_with("atomic_") => { use llvm::AtomicOrdering::*; let split: Vec<&str> = name.split('_').collect(); @@ -464,22 +392,25 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, _ => ccx.sess().fatal("Atomic intrinsic not in correct format"), }; + let invalid_monomorphization = |sty| { + span_invalid_monomorphization_error(tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", name, sty)); + }; + match split[1] { "cxchg" | "cxchgweak" => { let sty = &substs.type_at(0).sty; if int_type_width_signed(sty, ccx).is_some() { let weak = if split[1] == "cxchgweak" { llvm::True } else { llvm::False }; - let val = AtomicCmpXchg(bcx, llargs[0], llargs[1], llargs[2], - order, failorder, weak); - let result = ExtractValue(bcx, val, 0); - let success = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx())); - Store(bcx, result, StructGEP(bcx, llresult, 0)); - Store(bcx, success, StructGEP(bcx, llresult, 1)); + let val = bcx.atomic_cmpxchg(llargs[0], llargs[1], llargs[2], order, + failorder, weak); + let result = bcx.extract_value(val, 0); + let success = bcx.zext(bcx.extract_value(val, 1), Type::bool(bcx.ccx)); + bcx.store(result, bcx.struct_gep(llresult, 0)); + bcx.store(success, bcx.struct_gep(llresult, 1)); } else { - span_invalid_monomorphization_error( - tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, sty)); + invalid_monomorphization(sty); } C_nil(ccx) } @@ -487,12 +418,9 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, "load" => { let sty = &substs.type_at(0).sty; if int_type_width_signed(sty, ccx).is_some() { - AtomicLoad(bcx, llargs[0], order) + bcx.atomic_load(llargs[0], order) } else { - span_invalid_monomorphization_error( - tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, sty)); + invalid_monomorphization(sty); C_nil(ccx) } } @@ -500,23 +428,20 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, "store" => { let sty = &substs.type_at(0).sty; if int_type_width_signed(sty, ccx).is_some() { - AtomicStore(bcx, llargs[1], llargs[0], order); + bcx.atomic_store(llargs[1], llargs[0], order); } else { - span_invalid_monomorphization_error( - tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, sty)); + invalid_monomorphization(sty); } C_nil(ccx) } "fence" => { - AtomicFence(bcx, order, llvm::SynchronizationScope::CrossThread); + bcx.atomic_fence(order, llvm::SynchronizationScope::CrossThread); C_nil(ccx) } "singlethreadfence" => { - AtomicFence(bcx, order, llvm::SynchronizationScope::SingleThread); + bcx.atomic_fence(order, llvm::SynchronizationScope::SingleThread); C_nil(ccx) } @@ -539,20 +464,16 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let sty = &substs.type_at(0).sty; if int_type_width_signed(sty, ccx).is_some() { - AtomicRMW(bcx, atom_op, llargs[0], llargs[1], order) + bcx.atomic_rmw(atom_op, llargs[0], llargs[1], order) } else { - span_invalid_monomorphization_error( - tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, sty)); + invalid_monomorphization(sty); C_nil(ccx) } } } - } - (..) => { + _ => { let intr = match Intrinsic::find(&name) { Some(intr) => intr, None => bug!("unknown intrinsic '{}'", name), @@ -581,18 +502,15 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, *any_changes_needed |= llvm_elem.is_some(); let t = llvm_elem.as_ref().unwrap_or(t); - let elem = one(ty_to_type(ccx, t, - any_changes_needed)); + let elem = one(ty_to_type(ccx, t, any_changes_needed)); vec![elem.ptr_to()] } Vector(ref t, ref llvm_elem, length) => { *any_changes_needed |= llvm_elem.is_some(); let t = llvm_elem.as_ref().unwrap_or(t); - let elem = one(ty_to_type(ccx, t, - any_changes_needed)); - vec![Type::vector(&elem, - length as u64)] + let elem = one(ty_to_type(ccx, t, any_changes_needed)); + vec![Type::vector(&elem, length as u64)] } Aggregate(false, ref contents) => { let elems = contents.iter() @@ -613,11 +531,11 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, // qux` to be converted into `foo, bar, baz, qux`, integer // arguments to be truncated as needed and pointers to be // cast. - fn modify_as_needed<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - t: &intrinsics::Type, - arg_type: Ty<'tcx>, - llarg: ValueRef) - -> Vec<ValueRef> + fn modify_as_needed<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + t: &intrinsics::Type, + arg_type: Ty<'tcx>, + llarg: ValueRef) + -> Vec<ValueRef> { match *t { intrinsics::Type::Aggregate(true, ref contents) => { @@ -627,29 +545,27 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, // This assumes the type is "simple", i.e. no // destructors, and the contents are SIMD // etc. - assert!(!bcx.fcx.type_needs_drop(arg_type)); + assert!(!bcx.ccx.shared().type_needs_drop(arg_type)); let arg = adt::MaybeSizedValue::sized(llarg); (0..contents.len()) .map(|i| { - Load(bcx, adt::trans_field_ptr(bcx, arg_type, arg, Disr(0), i)) + bcx.load(adt::trans_field_ptr(bcx, arg_type, arg, Disr(0), i)) }) .collect() } intrinsics::Type::Pointer(_, Some(ref llvm_elem), _) => { - let llvm_elem = one(ty_to_type(bcx.ccx(), llvm_elem, &mut false)); - vec![PointerCast(bcx, llarg, - llvm_elem.ptr_to())] + let llvm_elem = one(ty_to_type(bcx.ccx, llvm_elem, &mut false)); + vec![bcx.pointercast(llarg, llvm_elem.ptr_to())] } intrinsics::Type::Vector(_, Some(ref llvm_elem), length) => { - let llvm_elem = one(ty_to_type(bcx.ccx(), llvm_elem, &mut false)); - vec![BitCast(bcx, llarg, - Type::vector(&llvm_elem, length as u64))] + let llvm_elem = one(ty_to_type(bcx.ccx, llvm_elem, &mut false)); + vec![bcx.bitcast(llarg, Type::vector(&llvm_elem, length as u64))] } intrinsics::Type::Integer(_, width, llvm_width) if width != llvm_width => { // the LLVM intrinsic uses a smaller integer // size than the C intrinsic's signature, so // we have to trim it down here. - vec![Trunc(bcx, llarg, Type::ix(bcx.ccx(), llvm_width as u64))] + vec![bcx.trunc(llarg, Type::ix(bcx.ccx, llvm_width as u64))] } _ => vec![llarg], } @@ -686,7 +602,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let f = declare::declare_cfn(ccx, name, Type::func(&inputs, &outputs)); - Call(bcx, f, &llargs, call_debug_location) + bcx.call(f, &llargs, None) } }; @@ -696,8 +612,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, assert!(!flatten); for i in 0..elems.len() { - let val = ExtractValue(bcx, val, i); - Store(bcx, val, StructGEP(bcx, llresult, i)); + let val = bcx.extract_value(val, i); + bcx.store(val, bcx.struct_gep(llresult, i)); } C_nil(ccx) } @@ -706,11 +622,10 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } }; - if val_ty(llval) != Type::void(ccx) && - machine::llsize_of_alloc(ccx, val_ty(llval)) != 0 { + if val_ty(llval) != Type::void(ccx) && machine::llsize_of_alloc(ccx, val_ty(llval)) != 0 { if let Some(ty) = fn_ty.ret.cast { - let ptr = PointerCast(bcx, llresult, ty.ptr_to()); - let store = Store(bcx, llval, ptr); + let ptr = bcx.pointercast(llresult, ty.ptr_to()); + let store = bcx.store(llval, ptr); unsafe { llvm::LLVMSetAlignment(store, type_of::align_of(ccx, ret_ty)); } @@ -718,20 +633,17 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, store_ty(bcx, llval, llresult, ret_ty); } } - - Result::new(bcx, llresult) } -fn copy_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - allow_overlap: bool, - volatile: bool, - tp_ty: Ty<'tcx>, - dst: ValueRef, - src: ValueRef, - count: ValueRef, - call_debug_location: DebugLoc) - -> ValueRef { - let ccx = bcx.ccx(); +fn copy_intrinsic<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + allow_overlap: bool, + volatile: bool, + tp_ty: Ty<'tcx>, + dst: ValueRef, + src: ValueRef, + count: ValueRef) + -> ValueRef { + let ccx = bcx.ccx; let lltp_ty = type_of::type_of(ccx, tp_ty); let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); let size = machine::llsize_of(ccx, lltp_ty); @@ -745,92 +657,49 @@ fn copy_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let name = format!("llvm.{}.p0i8.p0i8.i{}", operation, int_size); - let dst_ptr = PointerCast(bcx, dst, Type::i8p(ccx)); - let src_ptr = PointerCast(bcx, src, Type::i8p(ccx)); + let dst_ptr = bcx.pointercast(dst, Type::i8p(ccx)); + let src_ptr = bcx.pointercast(src, Type::i8p(ccx)); let llfn = ccx.get_intrinsic(&name); - Call(bcx, - llfn, - &[dst_ptr, - src_ptr, - Mul(bcx, size, count, DebugLoc::None), - align, - C_bool(ccx, volatile)], - call_debug_location) + bcx.call(llfn, + &[dst_ptr, + src_ptr, + bcx.mul(size, count), + align, + C_bool(ccx, volatile)], + None) } -fn memset_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - volatile: bool, - tp_ty: Ty<'tcx>, - dst: ValueRef, - val: ValueRef, - count: ValueRef, - call_debug_location: DebugLoc) - -> ValueRef { - let ccx = bcx.ccx(); - let lltp_ty = type_of::type_of(ccx, tp_ty); - let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); +fn memset_intrinsic<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + volatile: bool, + ty: Ty<'tcx>, + dst: ValueRef, + val: ValueRef, + count: ValueRef +) -> ValueRef { + let ccx = bcx.ccx; + let align = C_i32(ccx, type_of::align_of(ccx, ty) as i32); + let lltp_ty = type_of::type_of(ccx, ty); let size = machine::llsize_of(ccx, lltp_ty); - let int_size = machine::llbitsize_of_real(ccx, ccx.int_type()); - - let name = format!("llvm.memset.p0i8.i{}", int_size); - - let dst_ptr = PointerCast(bcx, dst, Type::i8p(ccx)); - let llfn = ccx.get_intrinsic(&name); - - Call(bcx, - llfn, - &[dst_ptr, - val, - Mul(bcx, size, count, DebugLoc::None), - align, - C_bool(ccx, volatile)], - call_debug_location) + let dst = bcx.pointercast(dst, Type::i8p(ccx)); + call_memset(bcx, dst, val, bcx.mul(size, count), align, volatile) } -fn count_zeros_intrinsic(bcx: Block, - name: &str, - val: ValueRef, - call_debug_location: DebugLoc) - -> ValueRef { - let y = C_bool(bcx.ccx(), false); - let llfn = bcx.ccx().get_intrinsic(&name); - Call(bcx, llfn, &[val, y], call_debug_location) -} - -fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - name: &str, - a: ValueRef, - b: ValueRef, - out: ValueRef, - call_debug_location: DebugLoc) - -> ValueRef { - let llfn = bcx.ccx().get_intrinsic(&name); - - // Convert `i1` to a `bool`, and write it to the out parameter - let val = Call(bcx, llfn, &[a, b], call_debug_location); - let result = ExtractValue(bcx, val, 0); - let overflow = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx())); - Store(bcx, result, StructGEP(bcx, out, 0)); - Store(bcx, overflow, StructGEP(bcx, out, 1)); - - C_nil(bcx.ccx()) -} - -fn try_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - func: ValueRef, - data: ValueRef, - local_ptr: ValueRef, - dest: ValueRef, - dloc: DebugLoc) -> Block<'blk, 'tcx> { +fn try_intrinsic<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + func: ValueRef, + data: ValueRef, + local_ptr: ValueRef, + dest: ValueRef, +) { if bcx.sess().no_landing_pads() { - Call(bcx, func, &[data], dloc); - Store(bcx, C_null(Type::i8p(bcx.ccx())), dest); - bcx + bcx.call(func, &[data], None); + bcx.store(C_null(Type::i8p(&bcx.ccx)), dest); } else if wants_msvc_seh(bcx.sess()) { - trans_msvc_try(bcx, func, data, local_ptr, dest, dloc) + trans_msvc_try(bcx, func, data, local_ptr, dest); } else { - trans_gnu_try(bcx, func, data, local_ptr, dest, dloc) + trans_gnu_try(bcx, func, data, local_ptr, dest); } } @@ -841,26 +710,24 @@ fn try_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // instructions are meant to work for all targets, as of the time of this // writing, however, LLVM does not recommend the usage of these new instructions // as the old ones are still more optimized. -fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - func: ValueRef, - data: ValueRef, - local_ptr: ValueRef, - dest: ValueRef, - dloc: DebugLoc) -> Block<'blk, 'tcx> { - let llfn = get_rust_try_fn(bcx.fcx, &mut |bcx| { - let ccx = bcx.ccx(); - let dloc = DebugLoc::None; - - SetPersonalityFn(bcx, bcx.fcx.eh_personality()); - - let normal = bcx.fcx.new_block("normal"); - let catchswitch = bcx.fcx.new_block("catchswitch"); - let catchpad = bcx.fcx.new_block("catchpad"); - let caught = bcx.fcx.new_block("caught"); - - let func = llvm::get_param(bcx.fcx.llfn, 0); - let data = llvm::get_param(bcx.fcx.llfn, 1); - let local_ptr = llvm::get_param(bcx.fcx.llfn, 2); +fn trans_msvc_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + func: ValueRef, + data: ValueRef, + local_ptr: ValueRef, + dest: ValueRef) { + let llfn = get_rust_try_fn(bcx.fcx(), &mut |bcx| { + let ccx = bcx.ccx; + + bcx.set_personality_fn(bcx.ccx.eh_personality()); + + let normal = bcx.fcx().build_new_block("normal"); + let catchswitch = bcx.fcx().build_new_block("catchswitch"); + let catchpad = bcx.fcx().build_new_block("catchpad"); + let caught = bcx.fcx().build_new_block("caught"); + + let func = llvm::get_param(bcx.fcx().llfn, 0); + let data = llvm::get_param(bcx.fcx().llfn, 1); + let local_ptr = llvm::get_param(bcx.fcx().llfn, 2); // We're generating an IR snippet that looks like: // @@ -902,37 +769,37 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // // More information can be found in libstd's seh.rs implementation. let i64p = Type::i64(ccx).ptr_to(); - let slot = Alloca(bcx, i64p, "slot"); - Invoke(bcx, func, &[data], normal.llbb, catchswitch.llbb, dloc); + let slot = bcx.fcx().alloca(i64p, "slot"); + bcx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), + None); - Ret(normal, C_i32(ccx, 0), dloc); + normal.ret(C_i32(ccx, 0)); - let cs = CatchSwitch(catchswitch, None, None, 1); - AddHandler(catchswitch, cs, catchpad.llbb); + let cs = catchswitch.catch_switch(None, None, 1); + catchswitch.add_handler(cs, catchpad.llbb()); let tcx = ccx.tcx(); let tydesc = match tcx.lang_items.msvc_try_filter() { Some(did) => ::consts::get_static(ccx, did), None => bug!("msvc_try_filter not defined"), }; - let tok = CatchPad(catchpad, cs, &[tydesc, C_i32(ccx, 0), slot]); - let addr = Load(catchpad, slot); - let arg1 = Load(catchpad, addr); + let tok = catchpad.catch_pad(cs, &[tydesc, C_i32(ccx, 0), slot]); + let addr = catchpad.load(slot); + let arg1 = catchpad.load(addr); let val1 = C_i32(ccx, 1); - let arg2 = Load(catchpad, InBoundsGEP(catchpad, addr, &[val1])); - let local_ptr = BitCast(catchpad, local_ptr, i64p); - Store(catchpad, arg1, local_ptr); - Store(catchpad, arg2, InBoundsGEP(catchpad, local_ptr, &[val1])); - CatchRet(catchpad, tok, caught.llbb); + let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1])); + let local_ptr = catchpad.bitcast(local_ptr, i64p); + catchpad.store(arg1, local_ptr); + catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1])); + catchpad.catch_ret(tok, caught.llbb()); - Ret(caught, C_i32(ccx, 1), dloc); + caught.ret(C_i32(ccx, 1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = Call(bcx, llfn, &[func, data, local_ptr], dloc); - Store(bcx, ret, dest); - return bcx + let ret = bcx.call(llfn, &[func, data, local_ptr], None); + bcx.store(ret, dest); } // Definition of the standard "try" function for Rust using the GNU-like model @@ -946,15 +813,13 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // function calling it, and that function may already have other personality // functions in play. By calling a shim we're guaranteed that our shim will have // the right personality function. -fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - func: ValueRef, - data: ValueRef, - local_ptr: ValueRef, - dest: ValueRef, - dloc: DebugLoc) -> Block<'blk, 'tcx> { - let llfn = get_rust_try_fn(bcx.fcx, &mut |bcx| { - let ccx = bcx.ccx(); - let dloc = DebugLoc::None; +fn trans_gnu_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + func: ValueRef, + data: ValueRef, + local_ptr: ValueRef, + dest: ValueRef) { + let llfn = get_rust_try_fn(bcx.fcx(), &mut |bcx| { + let ccx = bcx.ccx; // Translates the shims described above: // @@ -973,14 +838,14 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // expected to be `*mut *mut u8` for this to actually work, but that's // managed by the standard library. - let then = bcx.fcx.new_block("then"); - let catch = bcx.fcx.new_block("catch"); + let then = bcx.fcx().build_new_block("then"); + let catch = bcx.fcx().build_new_block("catch"); - let func = llvm::get_param(bcx.fcx.llfn, 0); - let data = llvm::get_param(bcx.fcx.llfn, 1); - let local_ptr = llvm::get_param(bcx.fcx.llfn, 2); - Invoke(bcx, func, &[data], then.llbb, catch.llbb, dloc); - Ret(then, C_i32(ccx, 0), dloc); + let func = llvm::get_param(bcx.fcx().llfn, 0); + let data = llvm::get_param(bcx.fcx().llfn, 1); + let local_ptr = llvm::get_param(bcx.fcx().llfn, 2); + bcx.invoke(func, &[data], then.llbb(), catch.llbb(), None); + then.ret(C_i32(ccx, 0)); // Type indicator for the exception being thrown. // @@ -990,18 +855,17 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // rust_try ignores the selector. let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); - let vals = LandingPad(catch, lpad_ty, bcx.fcx.eh_personality(), 1); - AddClause(catch, vals, C_null(Type::i8p(ccx))); - let ptr = ExtractValue(catch, vals, 0); - Store(catch, ptr, BitCast(catch, local_ptr, Type::i8p(ccx).ptr_to())); - Ret(catch, C_i32(ccx, 1), dloc); + let vals = catch.landing_pad(lpad_ty, bcx.ccx.eh_personality(), 1, catch.fcx().llfn); + catch.add_clause(vals, C_null(Type::i8p(ccx))); + let ptr = catch.extract_value(vals, 0); + catch.store(ptr, catch.bitcast(local_ptr, Type::i8p(ccx).ptr_to())); + catch.ret(C_i32(ccx, 1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = Call(bcx, llfn, &[func, data, local_ptr], dloc); - Store(bcx, ret, dest); - return bcx; + let ret = bcx.call(llfn, &[func, data, local_ptr], None); + bcx.store(ret, dest); } // Helper function to give a Block to a closure to translate a shim function. @@ -1010,11 +874,10 @@ fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, name: &str, inputs: Vec<Ty<'tcx>>, output: Ty<'tcx>, - trans: &mut for<'b> FnMut(Block<'b, 'tcx>)) + trans: &mut for<'b> FnMut(BlockAndBuilder<'b, 'tcx>)) -> ValueRef { let ccx = fcx.ccx; let sig = ccx.tcx().mk_fn_sig(inputs.into_iter(), output, false); - let fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]); let rust_fn_ty = ccx.tcx().mk_fn_ptr(ccx.tcx().mk_bare_fn(ty::BareFnTy { unsafety: hir::Unsafety::Unsafe, @@ -1022,11 +885,8 @@ fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, sig: ty::Binder(sig) })); let llfn = declare::define_internal_fn(ccx, name, rust_fn_ty); - let (fcx, block_arena); - block_arena = TypedArena::new(); - fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &block_arena); - trans(fcx.init(true)); - fcx.cleanup(); + let fcx = FunctionContext::new(ccx, llfn); + trans(fcx.get_entry_block()); llfn } @@ -1035,7 +895,7 @@ fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, // // This function is only generated once and is then cached. fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, - trans: &mut for<'b> FnMut(Block<'b, 'tcx>)) + trans: &mut for<'b> FnMut(BlockAndBuilder<'b, 'tcx>)) -> ValueRef { let ccx = fcx.ccx; if let Some(llfn) = ccx.rust_try_fn().get() { @@ -1060,16 +920,15 @@ fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { span_err!(a, b, E0511, "{}", c); } -fn generic_simd_intrinsic<'blk, 'tcx, 'a> - (bcx: Block<'blk, 'tcx>, - name: &str, - callee_ty: Ty<'tcx>, - llargs: &[ValueRef], - ret_ty: Ty<'tcx>, - llret_ty: Type, - call_debug_location: DebugLoc, - span: Span) -> ValueRef -{ +fn generic_simd_intrinsic<'a, 'tcx>( + bcx: &BlockAndBuilder<'a, 'tcx>, + name: &str, + callee_ty: Ty<'tcx>, + llargs: &[ValueRef], + ret_ty: Ty<'tcx>, + llret_ty: Type, + span: Span +) -> ValueRef { // macros for error handling: macro_rules! emit_error { ($msg: tt) => { @@ -1087,7 +946,7 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a> ($cond: expr, $($fmt: tt)*) => { if !$cond { emit_error!($($fmt)*); - return C_nil(bcx.ccx()) + return C_nil(bcx.ccx) } } } @@ -1138,8 +997,7 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a> llargs[1], in_elem, llret_ty, - cmp_op, - call_debug_location) + cmp_op) } if name.starts_with("simd_shuffle") { @@ -1179,7 +1037,7 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a> arg_idx, total_len); None } - Some(idx) => Some(C_i32(bcx.ccx(), idx as i32)), + Some(idx) => Some(C_i32(bcx.ccx, idx as i32)), } }) .collect(); @@ -1188,20 +1046,20 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a> None => return C_null(llret_ty) }; - return ShuffleVector(bcx, llargs[0], llargs[1], C_vector(&indices)) + return bcx.shuffle_vector(llargs[0], llargs[1], C_vector(&indices)) } if name == "simd_insert" { require!(in_elem == arg_tys[2], "expected inserted type `{}` (element of input `{}`), found `{}`", in_elem, in_ty, arg_tys[2]); - return InsertElement(bcx, llargs[0], llargs[2], llargs[1]) + return bcx.insert_element(llargs[0], llargs[2], llargs[1]) } if name == "simd_extract" { require!(ret_ty == in_elem, "expected return type `{}` (element of input `{}`), found `{}`", in_elem, in_ty, ret_ty); - return ExtractElement(bcx, llargs[0], llargs[1]) + return bcx.extract_element(llargs[0], llargs[1]) } if name == "simd_cast" { @@ -1237,34 +1095,34 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a> match (in_style, out_style) { (Style::Int(in_is_signed), Style::Int(_)) => { return match in_width.cmp(&out_width) { - Ordering::Greater => Trunc(bcx, llargs[0], llret_ty), + Ordering::Greater => bcx.trunc(llargs[0], llret_ty), Ordering::Equal => llargs[0], Ordering::Less => if in_is_signed { - SExt(bcx, llargs[0], llret_ty) + bcx.sext(llargs[0], llret_ty) } else { - ZExt(bcx, llargs[0], llret_ty) + bcx.zext(llargs[0], llret_ty) } } } (Style::Int(in_is_signed), Style::Float) => { return if in_is_signed { - SIToFP(bcx, llargs[0], llret_ty) + bcx.sitofp(llargs[0], llret_ty) } else { - UIToFP(bcx, llargs[0], llret_ty) + bcx.uitofp(llargs[0], llret_ty) } } (Style::Float, Style::Int(out_is_signed)) => { return if out_is_signed { - FPToSI(bcx, llargs[0], llret_ty) + bcx.fptosi(llargs[0], llret_ty) } else { - FPToUI(bcx, llargs[0], llret_ty) + bcx.fptoui(llargs[0], llret_ty) } } (Style::Float, Style::Float) => { return match in_width.cmp(&out_width) { - Ordering::Greater => FPTrunc(bcx, llargs[0], llret_ty), + Ordering::Greater => bcx.fptrunc(llargs[0], llret_ty), Ordering::Equal => llargs[0], - Ordering::Less => FPExt(bcx, llargs[0], llret_ty) + Ordering::Less => bcx.fpext(llargs[0], llret_ty) } } _ => {/* Unsupported. Fallthrough. */} @@ -1275,13 +1133,13 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a> ret_ty, out_elem); } macro_rules! arith { - ($($name: ident: $($($p: ident),* => $call: expr),*;)*) => { + ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { $( if name == stringify!($name) { match in_elem.sty { $( $(ty::$p(_))|* => { - return $call(bcx, llargs[0], llargs[1], call_debug_location) + return bcx.$call(llargs[0], llargs[1]) } )* _ => {}, @@ -1294,15 +1152,15 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a> } } arith! { - simd_add: TyUint, TyInt => Add, TyFloat => FAdd; - simd_sub: TyUint, TyInt => Sub, TyFloat => FSub; - simd_mul: TyUint, TyInt => Mul, TyFloat => FMul; - simd_div: TyFloat => FDiv; - simd_shl: TyUint, TyInt => Shl; - simd_shr: TyUint => LShr, TyInt => AShr; - simd_and: TyUint, TyInt => And; - simd_or: TyUint, TyInt => Or; - simd_xor: TyUint, TyInt => Xor; + simd_add: TyUint, TyInt => add, TyFloat => fadd; + simd_sub: TyUint, TyInt => sub, TyFloat => fsub; + simd_mul: TyUint, TyInt => mul, TyFloat => fmul; + simd_div: TyFloat => fdiv; + simd_shl: TyUint, TyInt => shl; + simd_shr: TyUint => lshr, TyInt => ashr; + simd_and: TyUint, TyInt => and; + simd_or: TyUint, TyInt => or; + simd_xor: TyUint, TyInt => xor; } span_bug!(span, "unknown SIMD intrinsic"); } diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index e2da635b159..3a8eef131a2 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -26,7 +26,6 @@ #![feature(associated_consts)] #![feature(box_patterns)] #![feature(box_syntax)] -#![feature(cell_extras)] #![feature(const_fn)] #![feature(custom_attribute)] #![allow(unused_attributes)] @@ -37,6 +36,7 @@ #![feature(slice_patterns)] #![feature(staged_api)] #![feature(unicode)] +#![feature(conservative_impl_trait)] use rustc::dep_graph::WorkProduct; @@ -96,8 +96,6 @@ mod asm; mod assert_module_sources; mod attributes; mod base; -mod basic_block; -mod build; mod builder; mod cabi_aarch64; mod cabi_arm; @@ -108,6 +106,7 @@ mod cabi_msp430; mod cabi_powerpc; mod cabi_powerpc64; mod cabi_s390x; +mod cabi_sparc; mod cabi_x86; mod cabi_x86_64; mod cabi_x86_win64; diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index aa9b900fa46..cf50e7be2af 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -9,16 +9,11 @@ // except according to those terms. use attributes; -use arena::TypedArena; use llvm::{ValueRef, get_params}; use rustc::traits; -use abi::FnType; -use base::*; -use build::*; -use callee::Callee; +use callee::{Callee, CalleeData}; use common::*; use consts; -use debuginfo::DebugLoc; use declare; use glue; use machine; @@ -32,15 +27,15 @@ use rustc::ty; const VTABLE_OFFSET: usize = 3; /// Extracts a method from a trait object's vtable, at the specified index. -pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - llvtable: ValueRef, - vtable_index: usize) - -> ValueRef { +pub fn get_virtual_method<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + llvtable: ValueRef, + vtable_index: usize) + -> ValueRef { // Load the data pointer from the object. debug!("get_virtual_method(vtable_index={}, llvtable={:?})", vtable_index, Value(llvtable)); - Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET])) + bcx.load(bcx.gepi(llvtable, &[vtable_index + VTABLE_OFFSET])) } /// Generate a shim function that allows an object type like `SomeTrait` to @@ -67,36 +62,47 @@ pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, callee: Callee<'tcx>) -> ValueRef { - let _icx = push_ctxt("trans_object_shim"); - let tcx = ccx.tcx(); - debug!("trans_object_shim({:?})", callee); - let (sig, abi, function_name) = match callee.ty.sty { - ty::TyFnDef(def_id, substs, f) => { + let function_name = match callee.ty.sty { + ty::TyFnDef(def_id, substs, _) => { let instance = Instance::new(def_id, substs); - (&f.sig, f.abi, instance.symbol_name(ccx.shared())) + instance.symbol_name(ccx.shared()) } _ => bug!() }; - let sig = tcx.erase_late_bound_regions_and_normalize(sig); - let fn_ty = FnType::new(ccx, abi, &sig, &[]); - let llfn = declare::define_internal_fn(ccx, &function_name, callee.ty); attributes::set_frame_pointer_elimination(ccx, llfn); - let (block_arena, fcx): (TypedArena<_>, FunctionContext); - block_arena = TypedArena::new(); - fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &block_arena); - let mut bcx = fcx.init(false); - - let dest = fcx.llretslotptr.get(); - let llargs = get_params(fcx.llfn); - bcx = callee.call(bcx, DebugLoc::None, - &llargs[fcx.fn_ty.ret.is_indirect() as usize..], dest).bcx; - - fcx.finish(bcx, DebugLoc::None); + let fcx = FunctionContext::new(ccx, llfn); + let bcx = fcx.get_entry_block(); + + let mut llargs = get_params(fcx.llfn); + let fn_ret = callee.ty.fn_ret(); + let fn_ty = callee.direct_fn_type(ccx, &[]); + + let fn_ptr = match callee.data { + CalleeData::Virtual(idx) => { + let fn_ptr = get_virtual_method(&bcx, + llargs.remove(fn_ty.ret.is_indirect() as usize + 1), idx); + let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); + bcx.pointercast(fn_ptr, llty) + }, + _ => bug!("trans_object_shim called with non-virtual callee"), + }; + let llret = bcx.call(fn_ptr, &llargs, None); + fn_ty.apply_attrs_callsite(llret); + + if fn_ret.0.is_never() { + bcx.unreachable(); + } else { + if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() { + bcx.ret_void(); + } else { + bcx.ret(llret); + } + } llfn } @@ -115,7 +121,6 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, -> ValueRef { let tcx = ccx.tcx(); - let _icx = push_ctxt("meth::get_vtable"); debug!("get_vtable(ty={:?}, trait_ref={:?})", ty, trait_ref); diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index e4d0533ec87..8df24da7135 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -16,31 +16,30 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::mir::{self, Location, TerminatorKind}; use rustc::mir::visit::{Visitor, LvalueContext}; use rustc::mir::traversal; -use common::{self, Block, BlockAndBuilder}; -use glue; +use common; +use super::MirContext; use super::rvalue; -pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>, - mir: &mir::Mir<'tcx>) -> BitVector { - let bcx = bcx.build(); - let mut analyzer = LocalAnalyzer::new(mir, &bcx); +pub fn lvalue_locals<'a, 'tcx>(mircx: &MirContext<'a, 'tcx>) -> BitVector { + let mir = mircx.mir; + let mut analyzer = LocalAnalyzer::new(mircx); analyzer.visit_mir(mir); for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() { - let ty = bcx.monomorphize(&ty); + let ty = mircx.monomorphize(&ty); debug!("local {} has type {:?}", index, ty); if ty.is_scalar() || ty.is_unique() || ty.is_region_ptr() || ty.is_simd() || - common::type_is_zero_size(bcx.ccx(), ty) + common::type_is_zero_size(mircx.ccx, ty) { // These sorts of types are immediates that we can store // in an ValueRef without an alloca. - assert!(common::type_is_immediate(bcx.ccx(), ty) || - common::type_is_fat_ptr(bcx.tcx(), ty)); - } else if common::type_is_imm_pair(bcx.ccx(), ty) { + assert!(common::type_is_immediate(mircx.ccx, ty) || + common::type_is_fat_ptr(mircx.ccx, ty)); + } else if common::type_is_imm_pair(mircx.ccx, ty) { // We allow pairs and uses of any of their 2 fields. } else { // These sorts of types require an alloca. Note that @@ -56,22 +55,18 @@ pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>, analyzer.lvalue_locals } -struct LocalAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> { - mir: &'mir mir::Mir<'tcx>, - bcx: &'mir BlockAndBuilder<'bcx, 'tcx>, +struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { + cx: &'mir MirContext<'a, 'tcx>, lvalue_locals: BitVector, seen_assigned: BitVector } -impl<'mir, 'bcx, 'tcx> LocalAnalyzer<'mir, 'bcx, 'tcx> { - fn new(mir: &'mir mir::Mir<'tcx>, - bcx: &'mir BlockAndBuilder<'bcx, 'tcx>) - -> LocalAnalyzer<'mir, 'bcx, 'tcx> { +impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { + fn new(mircx: &'mir MirContext<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> { LocalAnalyzer { - mir: mir, - bcx: bcx, - lvalue_locals: BitVector::new(mir.local_decls.len()), - seen_assigned: BitVector::new(mir.local_decls.len()) + cx: mircx, + lvalue_locals: BitVector::new(mircx.mir.local_decls.len()), + seen_assigned: BitVector::new(mircx.mir.local_decls.len()) } } @@ -87,7 +82,7 @@ impl<'mir, 'bcx, 'tcx> LocalAnalyzer<'mir, 'bcx, 'tcx> { } } -impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { +impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { fn visit_assign(&mut self, block: mir::BasicBlock, lvalue: &mir::Lvalue<'tcx>, @@ -97,7 +92,7 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { if let mir::Lvalue::Local(index) = *lvalue { self.mark_assigned(index); - if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) { + if !rvalue::rvalue_creates_operand(rvalue) { self.mark_as_lvalue(index); } } else { @@ -117,7 +112,7 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { literal: mir::Literal::Item { def_id, .. }, .. }), ref args, .. - } if Some(def_id) == self.bcx.tcx().lang_items.box_free_fn() => { + } if Some(def_id) == self.cx.ccx.tcx().lang_items.box_free_fn() => { // box_free(x) shares with `drop x` the property that it // is not guaranteed to be statically dominated by the // definition of x, so x must always be in an alloca. @@ -140,10 +135,10 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { // Allow uses of projections of immediate pair fields. if let mir::Lvalue::Projection(ref proj) = *lvalue { if let mir::Lvalue::Local(_) = proj.base { - let ty = proj.base.ty(self.mir, self.bcx.tcx()); + let ty = proj.base.ty(self.cx.mir, self.cx.ccx.tcx()); - let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx())); - if common::type_is_imm_pair(self.bcx.ccx(), ty) { + let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx())); + if common::type_is_imm_pair(self.cx.ccx, ty) { if let mir::ProjectionElem::Field(..) = proj.elem { if let LvalueContext::Consume = context { return; @@ -171,11 +166,11 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { } LvalueContext::Drop => { - let ty = lvalue.ty(self.mir, self.bcx.tcx()); - let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx())); + let ty = lvalue.ty(self.cx.mir, self.cx.ccx.tcx()); + let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx())); // Only need the lvalue if we're actually dropping it. - if glue::type_needs_drop(self.bcx.tcx(), ty) { + if self.cx.ccx.shared().type_needs_drop(ty) { self.mark_as_lvalue(index); } } @@ -200,10 +195,7 @@ pub enum CleanupKind { Internal { funclet: mir::BasicBlock } } -pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>, - mir: &mir::Mir<'tcx>) - -> IndexVec<mir::BasicBlock, CleanupKind> -{ +pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> { fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>, mir: &mir::Mir<'tcx>) { for (bb, data) in mir.basic_blocks().iter_enumerated() { diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 76f5f32b5dc..71ac7c0d252 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -8,20 +8,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::{self, ValueRef}; +use llvm::{self, ValueRef, BasicBlockRef}; use rustc_const_eval::{ErrKind, ConstEvalErr, note_const_eval_err}; use rustc::middle::lang_items; -use rustc::ty; +use rustc::ty::{self, layout}; use rustc::mir; use abi::{Abi, FnType, ArgType}; -use adt; -use base; -use build; +use adt::{self, MaybeSizedValue}; +use base::{self, Lifetime}; use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual}; -use common::{self, Block, BlockAndBuilder, LandingPad}; +use common::{self, BlockAndBuilder, Funclet}; use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; use consts; -use debuginfo::DebugLoc; use Disr; use machine::{llalign_of_min, llbitsize_of_real}; use meth; @@ -29,6 +27,7 @@ use type_of; use glue; use type_::Type; +use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashMap; use syntax::symbol::Symbol; @@ -39,21 +38,25 @@ use super::lvalue::{LvalueRef}; use super::operand::OperandRef; use super::operand::OperandValue::{Pair, Ref, Immediate}; -use std::cell::Ref as CellRef; - -impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { - pub fn trans_block(&mut self, bb: mir::BasicBlock) { - let mut bcx = self.bcx(bb); - let data = &CellRef::clone(&self.mir)[bb]; +impl<'a, 'tcx> MirContext<'a, 'tcx> { + pub fn trans_block(&mut self, bb: mir::BasicBlock, + funclets: &IndexVec<mir::BasicBlock, Option<Funclet>>) { + let mut bcx = self.build_block(bb); + let data = &self.mir[bb]; debug!("trans_block({:?}={:?})", bb, data); + let funclet = match self.cleanup_kinds[bb] { + CleanupKind::Internal { funclet } => funclets[funclet].as_ref(), + _ => funclets[bb].as_ref(), + }; + // Create the cleanup bundle, if needed. - let cleanup_pad = bcx.lpad().and_then(|lp| lp.cleanuppad()); - let cleanup_bundle = bcx.lpad().and_then(|l| l.bundle()); + let cleanup_pad = funclet.map(|lp| lp.cleanuppad()); + let cleanup_bundle = funclet.map(|l| l.bundle()); let funclet_br = |this: &Self, bcx: BlockAndBuilder, bb: mir::BasicBlock| { - let lltarget = this.blocks[bb].llbb; + let lltarget = this.blocks[bb]; if let Some(cp) = cleanup_pad { match this.cleanup_kinds[bb] { CleanupKind::Funclet => { @@ -70,7 +73,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }; let llblock = |this: &mut Self, target: mir::BasicBlock| { - let lltarget = this.blocks[target].llbb; + let lltarget = this.blocks[target]; if let Some(cp) = cleanup_pad { match this.cleanup_kinds[target] { @@ -79,8 +82,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug!("llblock: creating cleanup trampoline for {:?}", target); let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); - let trampoline = this.fcx.new_block(name).build(); - trampoline.set_personality_fn(this.fcx.eh_personality()); + let trampoline = this.fcx.build_new_block(name); trampoline.cleanup_ret(cp, Some(lltarget)); trampoline.llbb() } @@ -93,7 +95,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { (this.cleanup_kinds[bb], this.cleanup_kinds[target]) { // jump *into* cleanup - need a landing pad if GNU - this.landing_pad_to(target).llbb + this.landing_pad_to(target) } else { lltarget } @@ -108,23 +110,22 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug!("trans_block: terminator: {:?}", terminator); let span = terminator.source_info.span; - let debug_loc = self.debug_loc(terminator.source_info); - debug_loc.apply_to_bcx(&bcx); - debug_loc.apply(bcx.fcx()); + self.set_debug_loc(&bcx, terminator.source_info); match terminator.kind { mir::TerminatorKind::Resume => { if let Some(cleanup_pad) = cleanup_pad { bcx.cleanup_ret(cleanup_pad, None); } else { - let llpersonality = bcx.fcx().eh_personality(); - bcx.set_personality_fn(llpersonality); - let ps = self.get_personality_slot(&bcx); let lp = bcx.load(ps); - bcx.with_block(|bcx| { - base::call_lifetime_end(bcx, ps); - base::trans_unwind_resume(bcx, lp); - }); + Lifetime::End.call(&bcx, ps); + if !bcx.sess().target.target.options.custom_unwind_resume { + bcx.resume(lp); + } else { + let exc_ptr = bcx.extract_value(lp, 0); + bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], cleanup_bundle); + bcx.unreachable(); + } } } @@ -143,9 +144,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => { let discr_lvalue = self.trans_lvalue(&bcx, discr); let ty = discr_lvalue.ty.to_ty(bcx.tcx()); - let discr = bcx.with_block(|bcx| - adt::trans_get_discr(bcx, ty, discr_lvalue.llval, None, true) - ); + let discr = adt::trans_get_discr(&bcx, ty, discr_lvalue.llval, None, true); let mut bb_hist = FxHashMap(); for target in targets { @@ -162,16 +161,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // We're generating an exhaustive switch, so the else branch // can't be hit. Branching to an unreachable instruction // lets LLVM know this - _ => (None, self.unreachable_block().llbb) + _ => (None, self.unreachable_block()) }; let switch = bcx.switch(discr, default_blk, targets.len()); assert_eq!(adt_def.variants.len(), targets.len()); for (adt_variant, &target) in adt_def.variants.iter().zip(targets) { if default_bb != Some(target) { let llbb = llblock(self, target); - let llval = bcx.with_block(|bcx| adt::trans_case( - bcx, ty, Disr::from(adt_variant.disr_val))); - build::AddCase(switch, llval, llbb) + let llval = adt::trans_case(&bcx, ty, Disr::from(adt_variant.disr_val)); + bcx.add_case(switch, llval, llbb) } } } @@ -179,17 +177,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { let (otherwise, targets) = targets.split_last().unwrap(); let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval); - let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty)); + let discr = base::to_immediate(&bcx, discr, switch_ty); let switch = bcx.switch(discr, llblock(self, *otherwise), values.len()); for (value, target) in values.iter().zip(targets) { - let val = Const::from_constval(bcx.ccx(), value.clone(), switch_ty); + let val = Const::from_constval(bcx.ccx, value.clone(), switch_ty); let llbb = llblock(self, *target); - build::AddCase(switch, val.llval, llbb) + bcx.add_case(switch, val.llval, llbb) } } mir::TerminatorKind::Return => { - let ret = bcx.fcx().fn_ty.ret; + let ret = self.fn_ty.ret; if ret.is_ignore() || ret.is_indirect() { bcx.ret_void(); return; @@ -208,14 +206,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }; let llslot = match op.val { Immediate(_) | Pair(..) => { - let llscratch = build::AllocaFcx(bcx.fcx(), ret.original_ty, "ret"); + let llscratch = bcx.fcx().alloca(ret.original_ty, "ret"); self.store_operand(&bcx, llscratch, op); llscratch } Ref(llval) => llval }; let load = bcx.load(bcx.pointercast(llslot, cast_ty.ptr_to())); - let llalign = llalign_of_min(bcx.ccx(), ret.ty); + let llalign = llalign_of_min(bcx.ccx, ret.ty); unsafe { llvm::LLVMSetAlignment(load, llalign); } @@ -233,48 +231,38 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::TerminatorKind::Drop { ref location, target, unwind } => { let ty = location.ty(&self.mir, bcx.tcx()).to_ty(bcx.tcx()); - let ty = bcx.monomorphize(&ty); + let ty = self.monomorphize(&ty); // Double check for necessity to drop - if !glue::type_needs_drop(bcx.tcx(), ty) { + if !bcx.ccx.shared().type_needs_drop(ty) { funclet_br(self, bcx, target); return; } let lvalue = self.trans_lvalue(&bcx, location); - let drop_fn = glue::get_drop_glue(bcx.ccx(), ty); - let drop_ty = glue::get_drop_glue_type(bcx.tcx(), ty); - let is_sized = common::type_is_sized(bcx.tcx(), ty); - let llvalue = if is_sized { - if drop_ty != ty { - bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx(), drop_ty).ptr_to()) + let drop_fn = glue::get_drop_glue(bcx.ccx, ty); + let drop_ty = glue::get_drop_glue_type(bcx.ccx.shared(), ty); + let ptr = if bcx.ccx.shared().type_is_sized(ty) { + let value = if drop_ty != ty { + bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx, drop_ty).ptr_to()) } else { lvalue.llval - } + }; + MaybeSizedValue::sized(value) } else { - // FIXME(#36457) Currently drop glue takes sized - // values as a `*(data, meta)`, but elsewhere in - // MIR we pass `(data, meta)` as two separate - // arguments. It would be better to fix drop glue, - // but I am shooting for a quick fix to #35546 - // here that can be cleanly backported to beta, so - // I want to avoid touching all of trans. - bcx.with_block(|bcx| { - let scratch = base::alloc_ty(bcx, ty, "drop"); - base::call_lifetime_start(bcx, scratch); - build::Store(bcx, lvalue.llval, base::get_dataptr(bcx, scratch)); - build::Store(bcx, lvalue.llextra, base::get_meta(bcx, scratch)); - scratch - }) + MaybeSizedValue::unsized_(lvalue.llval, lvalue.llextra) }; + let args = &[ptr.value, ptr.meta][..1 + ptr.has_meta() as usize]; if let Some(unwind) = unwind { - bcx.invoke(drop_fn, - &[llvalue], - self.blocks[target].llbb, - llblock(self, unwind), - cleanup_bundle); + bcx.invoke( + drop_fn, + args, + self.blocks[target], + llblock(self, unwind), + cleanup_bundle + ); } else { - bcx.call(drop_fn, &[llvalue], cleanup_bundle); + bcx.call(drop_fn, args, cleanup_bundle); funclet_br(self, bcx, target); } } @@ -290,7 +278,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // NOTE: Unlike binops, negation doesn't have its own // checked operation, just a comparison with the minimum // value, so we have to check for the assert message. - if !bcx.ccx().check_overflow() { + if !bcx.ccx.check_overflow() { use rustc_const_math::ConstMathErr::Overflow; use rustc_const_math::Op::Neg; @@ -306,27 +294,27 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } // Pass the condition through llvm.expect for branch hinting. - let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1"); - let cond = bcx.call(expect, &[cond, C_bool(bcx.ccx(), expected)], None); + let expect = bcx.ccx.get_intrinsic(&"llvm.expect.i1"); + let cond = bcx.call(expect, &[cond, C_bool(bcx.ccx, expected)], None); // Create the failure block and the conditional branch to it. let lltarget = llblock(self, target); - let panic_block = self.fcx.new_block("panic"); + let panic_block = self.fcx.build_new_block("panic"); if expected { - bcx.cond_br(cond, lltarget, panic_block.llbb); + bcx.cond_br(cond, lltarget, panic_block.llbb()); } else { - bcx.cond_br(cond, panic_block.llbb, lltarget); + bcx.cond_br(cond, panic_block.llbb(), lltarget); } // After this point, bcx is the block for the call to panic. - bcx = panic_block.build(); - debug_loc.apply_to_bcx(&bcx); + bcx = panic_block; + self.set_debug_loc(&bcx, terminator.source_info); // Get the location information. let loc = bcx.sess().codemap().lookup_char_pos(span.lo); let filename = Symbol::intern(&loc.file.name).as_str(); - let filename = C_str_slice(bcx.ccx(), filename); - let line = C_u32(bcx.ccx(), loc.line as u32); + let filename = C_str_slice(bcx.ccx, filename); + let line = C_u32(bcx.ccx, loc.line as u32); // Put together the arguments to the panic entry point. let (lang_item, args, const_err) = match *msg { @@ -343,9 +331,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }) }); - let file_line = C_struct(bcx.ccx(), &[filename, line], false); - let align = llalign_of_min(bcx.ccx(), common::val_ty(file_line)); - let file_line = consts::addr_of(bcx.ccx(), + let file_line = C_struct(bcx.ccx, &[filename, line], false); + let align = llalign_of_min(bcx.ccx, common::val_ty(file_line)); + let file_line = consts::addr_of(bcx.ccx, file_line, align, "panic_bounds_check_loc"); @@ -355,12 +343,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::AssertMessage::Math(ref err) => { let msg_str = Symbol::intern(err.description()).as_str(); - let msg_str = C_str_slice(bcx.ccx(), msg_str); - let msg_file_line = C_struct(bcx.ccx(), + let msg_str = C_str_slice(bcx.ccx, msg_str); + let msg_file_line = C_struct(bcx.ccx, &[msg_str, filename, line], false); - let align = llalign_of_min(bcx.ccx(), common::val_ty(msg_file_line)); - let msg_file_line = consts::addr_of(bcx.ccx(), + let align = llalign_of_min(bcx.ccx, common::val_ty(msg_file_line)); + let msg_file_line = consts::addr_of(bcx.ccx, msg_file_line, align, "panic_loc"); @@ -384,15 +372,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // Obtain the panic entry point. let def_id = common::langcall(bcx.tcx(), Some(span), "", lang_item); - let callee = Callee::def(bcx.ccx(), def_id, - bcx.ccx().empty_substs_for_def_id(def_id)); - let llfn = callee.reify(bcx.ccx()); + let callee = Callee::def(bcx.ccx, def_id, + bcx.ccx.empty_substs_for_def_id(def_id)); + let llfn = callee.reify(bcx.ccx); // Translate the actual panic invoke/call. if let Some(unwind) = cleanup { bcx.invoke(llfn, &args, - self.unreachable_block().llbb, + self.unreachable_block(), llblock(self, unwind), cleanup_bundle); } else { @@ -411,7 +399,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let (mut callee, abi, sig) = match callee.ty.sty { ty::TyFnDef(def_id, substs, f) => { - (Callee::def(bcx.ccx(), def_id, substs), f.abi, &f.sig) + (Callee::def(bcx.ccx, def_id, substs), f.abi, &f.sig) } ty::TyFnPtr(f) => { (Callee { @@ -431,7 +419,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } _ => None }; - let intrinsic = intrinsic.as_ref().map(|s| &s[..]); + let mut intrinsic = intrinsic.as_ref().map(|s| &s[..]); if intrinsic == Some("move_val_init") { let &(_, target) = destination.as_ref().unwrap(); @@ -456,9 +444,29 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let extra_args = &args[sig.inputs().len()..]; let extra_args = extra_args.iter().map(|op_arg| { let op_ty = op_arg.ty(&self.mir, bcx.tcx()); - bcx.monomorphize(&op_ty) + self.monomorphize(&op_ty) }).collect::<Vec<_>>(); - let fn_ty = callee.direct_fn_type(bcx.ccx(), &extra_args); + let fn_ty = callee.direct_fn_type(bcx.ccx, &extra_args); + + if intrinsic == Some("drop_in_place") { + let &(_, target) = destination.as_ref().unwrap(); + let ty = if let ty::TyFnDef(_, substs, _) = callee.ty.sty { + substs.type_at(0) + } else { + bug!("Unexpected ty: {}", callee.ty); + }; + + // Double check for necessity to drop + if !bcx.ccx.shared().type_needs_drop(ty) { + funclet_br(self, bcx, target); + return; + } + + let drop_fn = glue::get_drop_glue(bcx.ccx, ty); + let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); + callee.data = Fn(bcx.pointercast(drop_fn, llty)); + intrinsic = None; + } // The arguments we'll be passing. Plus one to account for outptr, if used. let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; @@ -519,7 +527,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let fn_ptr = match callee.data { NamedTupleConstructor(_) => { // FIXME translate this like mir::Rvalue::Aggregate. - callee.reify(bcx.ccx()) + callee.reify(bcx.ccx) } Intrinsic => { use intrinsic::trans_intrinsic_call; @@ -537,10 +545,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bug!("Cannot use direct operand with an intrinsic call") }; - bcx.with_block(|bcx| { - trans_intrinsic_call(bcx, callee.ty, &fn_ty, - &llargs, dest, debug_loc); - }); + trans_intrinsic_call(&bcx, callee.ty, &fn_ty, &llargs, dest, + terminator.source_info.span); if let ReturnDest::IndirectOperand(dst, _) = ret_dest { // Make a fake operand for store_return @@ -554,8 +560,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { if let Some((_, target)) = *destination { funclet_br(self, bcx, target); } else { - // trans_intrinsic_call already used Unreachable. - // bcx.unreachable(); + bcx.unreachable(); } return; @@ -573,15 +578,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }; let invokeret = bcx.invoke(fn_ptr, &llargs, - ret_bcx.llbb, + ret_bcx, llblock(self, cleanup), cleanup_bundle); fn_ty.apply_attrs_callsite(invokeret); - if destination.is_some() { - let ret_bcx = ret_bcx.build(); + if let Some((_, target)) = *destination { + let ret_bcx = self.build_block(target); ret_bcx.at_start(|ret_bcx| { - debug_loc.apply_to_bcx(ret_bcx); + self.set_debug_loc(&ret_bcx, terminator.source_info); let op = OperandRef { val: Immediate(invokeret), ty: sig.output(), @@ -608,7 +613,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } fn trans_argument(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, op: OperandRef<'tcx>, llargs: &mut Vec<ValueRef>, fn_ty: &FnType, @@ -616,14 +621,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { callee: &mut CalleeData) { if let Pair(a, b) = op.val { // Treat the values in a fat pointer separately. - if common::type_is_fat_ptr(bcx.tcx(), op.ty) { + if common::type_is_fat_ptr(bcx.ccx, op.ty) { let (ptr, meta) = (a, b); if *next_idx == 0 { if let Virtual(idx) = *callee { - let llfn = bcx.with_block(|bcx| { - meth::get_virtual_method(bcx, meta, idx) - }); - let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to(); + let llfn = meth::get_virtual_method(bcx, meta, idx); + let llty = fn_ty.llvm_type(bcx.ccx).ptr_to(); *callee = Fn(bcx.pointercast(llfn, llty)); } } @@ -655,7 +658,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let (mut llval, by_ref) = match op.val { Immediate(_) | Pair(..) => { if arg.is_indirect() || arg.cast.is_some() { - let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg"); + let llscratch = bcx.fcx().alloca(arg.original_ty, "arg"); self.store_operand(bcx, llscratch, op); (llscratch, true) } else { @@ -667,13 +670,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { if by_ref && !arg.is_indirect() { // Have to load the argument, maybe while casting it. - if arg.original_ty == Type::i1(bcx.ccx()) { + if arg.original_ty == Type::i1(bcx.ccx) { // We store bools as i8 so we need to truncate to i1. llval = bcx.load_range_assert(llval, 0, 2, llvm::False); llval = bcx.trunc(llval, arg.original_ty); } else if let Some(ty) = arg.cast { llval = bcx.load(bcx.pointercast(llval, ty.ptr_to())); - let llalign = llalign_of_min(bcx.ccx(), arg.ty); + let llalign = llalign_of_min(bcx.ccx, arg.ty); unsafe { llvm::LLVMSetAlignment(llval, llalign); } @@ -686,7 +689,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } fn trans_arguments_untupled(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, operand: &mir::Operand<'tcx>, llargs: &mut Vec<ValueRef>, fn_ty: &FnType, @@ -705,9 +708,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { Ref(llval) => { let base = adt::MaybeSizedValue::sized(llval); for (n, &ty) in arg_types.iter().enumerate() { - let ptr = adt::trans_field_ptr_builder(bcx, tuple.ty, base, Disr(0), n); - let val = if common::type_is_fat_ptr(bcx.tcx(), ty) { - let (lldata, llextra) = base::load_fat_ptr_builder(bcx, ptr, ty); + let ptr = adt::trans_field_ptr(bcx, tuple.ty, base, Disr(0), n); + let val = if common::type_is_fat_ptr(bcx.ccx, ty) { + let (lldata, llextra) = base::load_fat_ptr(bcx, ptr, ty); Pair(lldata, llextra) } else { // trans_argument will load this if it needs to @@ -722,11 +725,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } Immediate(llval) => { + let l = bcx.ccx.layout_of(tuple.ty); + let v = if let layout::Univariant { ref variant, .. } = *l { + variant + } else { + bug!("Not a tuple."); + }; for (n, &ty) in arg_types.iter().enumerate() { - let mut elem = bcx.extract_value(llval, n); + let mut elem = bcx.extract_value(llval, v.memory_index[n] as usize); // Truncate bools to i1, if needed - if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx()) { - elem = bcx.trunc(elem, Type::i1(bcx.ccx())); + if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx) { + elem = bcx.trunc(elem, Type::i1(bcx.ccx)); } // If the tuple is immediate, the elements are as well let op = OperandRef { @@ -741,8 +750,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { for (n, &ty) in arg_types.iter().enumerate() { let mut elem = elems[n]; // Truncate bools to i1, if needed - if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx()) { - elem = bcx.trunc(elem, Type::i1(bcx.ccx())); + if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx) { + elem = bcx.trunc(elem, Type::i1(bcx.ccx)); } // Pair is always made up of immediates let op = OperandRef { @@ -756,91 +765,61 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } - fn get_personality_slot(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>) -> ValueRef { - let ccx = bcx.ccx(); + fn get_personality_slot(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>) -> ValueRef { + let ccx = bcx.ccx; if let Some(slot) = self.llpersonalityslot { slot } else { let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); - bcx.with_block(|bcx| { - let slot = base::alloca(bcx, llretty, "personalityslot"); - self.llpersonalityslot = Some(slot); - base::call_lifetime_start(bcx, slot); - slot - }) + let slot = bcx.fcx().alloca(llretty, "personalityslot"); + self.llpersonalityslot = Some(slot); + Lifetime::Start.call(bcx, slot); + slot } } /// Return the landingpad wrapper around the given basic block /// /// No-op in MSVC SEH scheme. - fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> Block<'bcx, 'tcx> - { + fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> BasicBlockRef { if let Some(block) = self.landing_pads[target_bb] { return block; } - if base::wants_msvc_seh(self.fcx.ccx.sess()) { + if base::wants_msvc_seh(self.ccx.sess()) { return self.blocks[target_bb]; } - let target = self.bcx(target_bb); + let target = self.build_block(target_bb); - let block = self.fcx.new_block("cleanup"); - self.landing_pads[target_bb] = Some(block); + let bcx = self.fcx.build_new_block("cleanup"); + self.landing_pads[target_bb] = Some(bcx.llbb()); - let bcx = block.build(); - let ccx = bcx.ccx(); - let llpersonality = self.fcx.eh_personality(); + let ccx = bcx.ccx; + let llpersonality = self.ccx.eh_personality(); let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); let llretval = bcx.landing_pad(llretty, llpersonality, 1, self.fcx.llfn); bcx.set_cleanup(llretval); let slot = self.get_personality_slot(&bcx); bcx.store(llretval, slot); bcx.br(target.llbb()); - block - } - - pub fn init_cpad(&mut self, bb: mir::BasicBlock) { - let bcx = self.bcx(bb); - let data = &self.mir[bb]; - debug!("init_cpad({:?})", data); - - match self.cleanup_kinds[bb] { - CleanupKind::NotCleanup => { - bcx.set_lpad(None) - } - _ if !base::wants_msvc_seh(bcx.sess()) => { - bcx.set_lpad(Some(LandingPad::gnu())) - } - CleanupKind::Internal { funclet } => { - // FIXME: is this needed? - bcx.set_personality_fn(self.fcx.eh_personality()); - bcx.set_lpad_ref(self.bcx(funclet).lpad()); - } - CleanupKind::Funclet => { - bcx.set_personality_fn(self.fcx.eh_personality()); - DebugLoc::None.apply_to_bcx(&bcx); - let cleanup_pad = bcx.cleanup_pad(None, &[]); - bcx.set_lpad(Some(LandingPad::msvc(cleanup_pad))); - } - }; + bcx.llbb() } - fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> { + fn unreachable_block(&mut self) -> BasicBlockRef { self.unreachable_block.unwrap_or_else(|| { - let bl = self.fcx.new_block("unreachable"); - bl.build().unreachable(); - self.unreachable_block = Some(bl); - bl + let bl = self.fcx.build_new_block("unreachable"); + bl.unreachable(); + self.unreachable_block = Some(bl.llbb()); + bl.llbb() }) } - fn bcx(&self, bb: mir::BasicBlock) -> BlockAndBuilder<'bcx, 'tcx> { - self.blocks[bb].build() + pub fn build_block(&self, bb: mir::BasicBlock) -> BlockAndBuilder<'a, 'tcx> { + BlockAndBuilder::new(self.blocks[bb], self.fcx) } - fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, + fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>, dest: &mir::Lvalue<'tcx>, fn_ret_ty: &ArgType, llargs: &mut Vec<ValueRef>, is_intrinsic: bool) -> ReturnDest { // If the return is ignored, we can just return a do-nothing ReturnDest @@ -857,18 +836,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { return if fn_ret_ty.is_indirect() { // Odd, but possible, case, we have an operand temporary, // but the calling convention has an indirect return. - let tmp = bcx.with_block(|bcx| { - base::alloc_ty(bcx, ret_ty, "tmp_ret") - }); + let tmp = base::alloc_ty(bcx, ret_ty, "tmp_ret"); llargs.push(tmp); ReturnDest::IndirectOperand(tmp, index) } else if is_intrinsic { // Currently, intrinsics always need a location to store // the result. so we create a temporary alloca for the // result - let tmp = bcx.with_block(|bcx| { - base::alloc_ty(bcx, ret_ty, "tmp_ret") - }); + let tmp = base::alloc_ty(bcx, ret_ty, "tmp_ret"); ReturnDest::IndirectOperand(tmp, index) } else { ReturnDest::DirectOperand(index) @@ -889,27 +864,27 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - fn trans_transmute(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, + fn trans_transmute(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>, src: &mir::Operand<'tcx>, dst: LvalueRef<'tcx>) { let mut val = self.trans_operand(bcx, src); if let ty::TyFnDef(def_id, substs, _) = val.ty.sty { - let llouttype = type_of::type_of(bcx.ccx(), dst.ty.to_ty(bcx.tcx())); - let out_type_size = llbitsize_of_real(bcx.ccx(), llouttype); + let llouttype = type_of::type_of(bcx.ccx, dst.ty.to_ty(bcx.tcx())); + let out_type_size = llbitsize_of_real(bcx.ccx, llouttype); if out_type_size != 0 { // FIXME #19925 Remove this hack after a release cycle. - let f = Callee::def(bcx.ccx(), def_id, substs); + let f = Callee::def(bcx.ccx, def_id, substs); let ty = match f.ty.sty { ty::TyFnDef(.., f) => bcx.tcx().mk_fn_ptr(f), _ => f.ty }; val = OperandRef { - val: Immediate(f.reify(bcx.ccx())), + val: Immediate(f.reify(bcx.ccx)), ty: ty }; } } - let llty = type_of::type_of(bcx.ccx(), val.ty); + let llty = type_of::type_of(bcx.ccx, val.ty); let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to()); self.store_operand(bcx, cast_ptr, val); } @@ -917,7 +892,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // Stores the return value of a function call into it's final location. fn store_return(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, dest: ReturnDest, ret_ty: ArgType, op: OperandRef<'tcx>) { @@ -933,9 +908,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { DirectOperand(index) => { // If there is a cast, we have to store and reload. let op = if ret_ty.cast.is_some() { - let tmp = bcx.with_block(|bcx| { - base::alloc_ty(bcx, op.ty, "tmp_ret") - }); + let tmp = base::alloc_ty(bcx, op.ty, "tmp_ret"); ret_ty.store(bcx, op.immediate(), tmp); self.trans_load(bcx, tmp, op.ty) } else { diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index bca81fa3645..08f68f8d49c 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -25,7 +25,7 @@ use rustc::ty::subst::Substs; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use {abi, adt, base, Disr, machine}; use callee::Callee; -use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty, type_is_sized}; +use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty}; use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral}; use common::{C_null, C_struct, C_str_slice, C_undef, C_uint}; use common::{const_to_opt_int, const_to_opt_uint}; @@ -401,7 +401,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { .projection_ty(tcx, &projection.elem); let base = tr_base.to_const(span); let projected_ty = self.monomorphize(&projected_ty).to_ty(tcx); - let is_sized = common::type_is_sized(tcx, projected_ty); + let is_sized = self.ccx.shared().type_is_sized(projected_ty); let (projected, llextra) = match projection.elem { mir::ProjectionElem::Deref => { @@ -598,11 +598,11 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::CastKind::Unsize => { // unsize targets other than to a fat pointer currently // can't be in constants. - assert!(common::type_is_fat_ptr(tcx, cast_ty)); + assert!(common::type_is_fat_ptr(self.ccx, cast_ty)); let pointee_ty = operand.ty.builtin_deref(true, ty::NoPreference) .expect("consts: unsizing got non-pointer type").ty; - let (base, old_info) = if !common::type_is_sized(tcx, pointee_ty) { + let (base, old_info) = if !self.ccx.shared().type_is_sized(pointee_ty) { // Normally, the source is a thin pointer and we are // adding extra info to make a fat pointer. The exception // is when we are upcasting an existing object fat pointer @@ -685,9 +685,9 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::CastKind::Misc => { // Casts from a fat-ptr. let ll_cast_ty = type_of::immediate_type_of(self.ccx, cast_ty); let ll_from_ty = type_of::immediate_type_of(self.ccx, operand.ty); - if common::type_is_fat_ptr(tcx, operand.ty) { + if common::type_is_fat_ptr(self.ccx, operand.ty) { let (data_ptr, meta_ptr) = operand.get_fat_ptr(); - if common::type_is_fat_ptr(tcx, cast_ty) { + if common::type_is_fat_ptr(self.ccx, cast_ty) { let ll_cft = ll_cast_ty.field_types(); let ll_fft = ll_from_ty.field_types(); let data_cast = consts::ptrcast(data_ptr, ll_cft[0]); @@ -716,7 +716,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { let base = match tr_lvalue.base { Base::Value(llval) => { // FIXME: may be wrong for &*(&simd_vec as &fmt::Debug) - let align = if type_is_sized(self.ccx.tcx(), ty) { + let align = if self.ccx.shared().type_is_sized(ty) { type_of::align_of(self.ccx, ty) } else { self.ccx.tcx().data_layout.pointer_align.abi() as machine::llalign @@ -731,7 +731,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { Base::Static(llval) => llval }; - let ptr = if common::type_is_sized(tcx, ty) { + let ptr = if self.ccx.shared().type_is_sized(ty) { base } else { C_struct(self.ccx, &[base, tr_lvalue.llextra], false) @@ -945,40 +945,39 @@ pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { +impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_constant(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, constant: &mir::Constant<'tcx>) -> Const<'tcx> { debug!("trans_constant({:?})", constant); - let ty = bcx.monomorphize(&constant.ty); + let ty = self.monomorphize(&constant.ty); let result = match constant.literal.clone() { mir::Literal::Item { def_id, substs } => { // Shortcut for zero-sized types, including function item // types, which would not work with MirConstContext. - if common::type_is_zero_size(bcx.ccx(), ty) { - let llty = type_of::type_of(bcx.ccx(), ty); + if common::type_is_zero_size(bcx.ccx, ty) { + let llty = type_of::type_of(bcx.ccx, ty); return Const::new(C_null(llty), ty); } - let substs = bcx.monomorphize(&substs); + let substs = self.monomorphize(&substs); let instance = Instance::new(def_id, substs); - MirConstContext::trans_def(bcx.ccx(), instance, IndexVec::new()) + MirConstContext::trans_def(bcx.ccx, instance, IndexVec::new()) } mir::Literal::Promoted { index } => { let mir = &self.mir.promoted[index]; - MirConstContext::new(bcx.ccx(), mir, bcx.fcx().param_substs, - IndexVec::new()).trans() + MirConstContext::new(bcx.ccx, mir, self.param_substs, IndexVec::new()).trans() } mir::Literal::Value { value } => { - Ok(Const::from_constval(bcx.ccx(), value, ty)) + Ok(Const::from_constval(bcx.ccx, value, ty)) } }; let result = result.unwrap_or_else(|_| { // We've errored, so we don't have to produce working code. - let llty = type_of::type_of(bcx.ccx(), ty); + let llty = type_of::type_of(bcx.ccx, ty); Const::new(C_undef(llty), ty) }); diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index d28c466e230..0cd7f007c5d 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -44,13 +44,13 @@ impl<'tcx> LvalueRef<'tcx> { LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty } } - pub fn alloca<'bcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, + pub fn alloca<'a>(bcx: &BlockAndBuilder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> LvalueRef<'tcx> { assert!(!ty.has_erasable_regions()); - let lltemp = bcx.with_block(|bcx| base::alloc_ty(bcx, ty, name)); + let lltemp = base::alloc_ty(bcx, ty, name); LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty)) } @@ -67,14 +67,14 @@ impl<'tcx> LvalueRef<'tcx> { } } -impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { +impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_lvalue(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, lvalue: &mir::Lvalue<'tcx>) -> LvalueRef<'tcx> { debug!("trans_lvalue(lvalue={:?})", lvalue); - let ccx = bcx.ccx(); + let ccx = bcx.ccx; let tcx = bcx.tcx(); if let mir::Lvalue::Local(index) = *lvalue { @@ -103,7 +103,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let ptr = self.trans_consume(bcx, base); let projected_ty = LvalueTy::from_ty(ptr.ty) .projection_ty(tcx, &mir::ProjectionElem::Deref); - let projected_ty = bcx.monomorphize(&projected_ty); + let projected_ty = self.monomorphize(&projected_ty); let (llptr, llextra) = match ptr.val { OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), OperandValue::Pair(llptr, llextra) => (llptr, llextra), @@ -118,14 +118,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Lvalue::Projection(ref projection) => { let tr_base = self.trans_lvalue(bcx, &projection.base); let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); - let projected_ty = bcx.monomorphize(&projected_ty); + let projected_ty = self.monomorphize(&projected_ty); let project_index = |llindex| { let element = if let ty::TySlice(_) = tr_base.ty.to_ty(tcx).sty { // Slices already point to the array element type. bcx.inbounds_gep(tr_base.llval, &[llindex]) } else { - let zero = common::C_uint(bcx.ccx(), 0u64); + let zero = common::C_uint(bcx.ccx, 0u64); bcx.inbounds_gep(tr_base.llval, &[zero, llindex]) }; element @@ -140,14 +140,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v, }; let discr = discr as u64; - let is_sized = common::type_is_sized(tcx, projected_ty.to_ty(tcx)); + let is_sized = self.ccx.shared().type_is_sized(projected_ty.to_ty(tcx)); let base = if is_sized { adt::MaybeSizedValue::sized(tr_base.llval) } else { adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra) }; - let llprojected = adt::trans_field_ptr_builder(bcx, base_ty, base, - Disr(discr), field.index()); + let llprojected = adt::trans_field_ptr(bcx, base_ty, base, Disr(discr), + field.index()); let llextra = if is_sized { ptr::null_mut() } else { @@ -162,19 +162,19 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::ProjectionElem::ConstantIndex { offset, from_end: false, min_length: _ } => { - let lloffset = C_uint(bcx.ccx(), offset); + let lloffset = C_uint(bcx.ccx, offset); (project_index(lloffset), ptr::null_mut()) } mir::ProjectionElem::ConstantIndex { offset, from_end: true, min_length: _ } => { - let lloffset = C_uint(bcx.ccx(), offset); - let lllen = tr_base.len(bcx.ccx()); + let lloffset = C_uint(bcx.ccx, offset); + let lllen = tr_base.len(bcx.ccx); let llindex = bcx.sub(lllen, lloffset); (project_index(llindex), ptr::null_mut()) } mir::ProjectionElem::Subslice { from, to } => { - let llindex = C_uint(bcx.ccx(), from); + let llindex = C_uint(bcx.ccx, from); let llbase = project_index(llindex); let base_ty = tr_base.ty.to_ty(bcx.tcx()); @@ -183,14 +183,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // must cast the lvalue pointer type to the new // array type (*[%_; new_len]). let base_ty = self.monomorphized_lvalue_ty(lvalue); - let llbasety = type_of::type_of(bcx.ccx(), base_ty).ptr_to(); + let llbasety = type_of::type_of(bcx.ccx, base_ty).ptr_to(); let llbase = bcx.pointercast(llbase, llbasety); (llbase, ptr::null_mut()) } ty::TySlice(..) => { assert!(tr_base.llextra != ptr::null_mut()); let lllen = bcx.sub(tr_base.llextra, - C_uint(bcx.ccx(), from+to)); + C_uint(bcx.ccx, from+to)); (llbase, lllen) } _ => bug!("unexpected type {:?} in Subslice", base_ty) @@ -214,7 +214,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // Perform an action using the given Lvalue. // If the Lvalue is an empty LocalRef::Operand, then a temporary stack slot // is created first, then used as an operand to update the Lvalue. - pub fn with_lvalue_ref<F, U>(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, + pub fn with_lvalue_ref<F, U>(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>, lvalue: &mir::Lvalue<'tcx>, f: F) -> U where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U { @@ -235,9 +235,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // See comments in LocalRef::new_operand as to why // we always have Some in a ZST LocalRef::Operand. let ty = self.monomorphized_lvalue_ty(lvalue); - if common::type_is_zero_size(bcx.ccx(), ty) { + if common::type_is_zero_size(bcx.ccx, ty) { // Pass an undef pointer as no stores can actually occur. - let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to()); + let llptr = C_undef(type_of(bcx.ccx, ty).ptr_to()); f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty))) } else { bug!("Lvalue local already set"); @@ -255,13 +255,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { /// /// nmatsakis: is this still necessary? Not sure. fn prepare_index(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, llindex: ValueRef) -> ValueRef { - let ccx = bcx.ccx(); - let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex)); - let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type()); + let ccx = bcx.ccx; + let index_size = machine::llbitsize_of_real(bcx.ccx, common::val_ty(llindex)); + let int_size = machine::llbitsize_of_real(bcx.ccx, ccx.int_type()); if index_size < int_size { bcx.zext(llindex, ccx.int_type()) } else if index_size > int_size { @@ -272,8 +272,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } pub fn monomorphized_lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { - let tcx = self.fcx.ccx.tcx(); + let tcx = self.ccx.tcx(); let lvalue_ty = lvalue.ty(&self.mir, tcx); - self.fcx.monomorphize(&lvalue_ty.to_ty(tcx)) + self.monomorphize(&lvalue_ty.to_ty(tcx)) } } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 12cbfcef7d2..7a50e5cbe8c 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -9,41 +9,50 @@ // except according to those terms. use libc::c_uint; -use llvm::{self, ValueRef}; -use rustc::ty; -use rustc::mir; +use llvm::{self, ValueRef, BasicBlockRef}; +use llvm::debuginfo::DIScope; +use rustc::ty::{self, layout}; +use rustc::mir::{self, Mir}; use rustc::mir::tcx::LvalueTy; +use rustc::ty::subst::Substs; +use rustc::infer::TransNormalize; +use rustc::ty::TypeFoldable; use session::config::FullDebugInfo; use base; -use common::{self, Block, BlockAndBuilder, CrateContext, FunctionContext, C_null}; -use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind, FunctionDebugContext}; -use machine; +use common::{self, BlockAndBuilder, CrateContext, FunctionContext, C_null, Funclet}; +use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext}; +use monomorphize::{self, Instance}; +use abi::FnType; use type_of; -use syntax_pos::{DUMMY_SP, NO_EXPANSION, COMMAND_LINE_EXPN, BytePos}; +use syntax_pos::{DUMMY_SP, NO_EXPANSION, COMMAND_LINE_EXPN, BytePos, Span}; use syntax::symbol::keywords; +use syntax::abi::Abi; -use std::cell::Ref; use std::iter; -use basic_block::BasicBlock; - use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; pub use self::constant::trans_static_initializer; +use self::analyze::CleanupKind; use self::lvalue::{LvalueRef}; use rustc::mir::traversal; use self::operand::{OperandRef, OperandValue}; /// Master context for translating MIR. -pub struct MirContext<'bcx, 'tcx:'bcx> { - mir: Ref<'tcx, mir::Mir<'tcx>>, +pub struct MirContext<'a, 'tcx:'a> { + mir: &'a mir::Mir<'tcx>, + + debug_context: debuginfo::FunctionDebugContext, + + fcx: &'a common::FunctionContext<'a, 'tcx>, - /// Function context - fcx: &'bcx common::FunctionContext<'bcx, 'tcx>, + ccx: &'a CrateContext<'a, 'tcx>, + + fn_ty: FnType, /// When unwinding is initiated, we have to store this personality /// value somewhere so that we can load it and re-use it in the @@ -55,17 +64,17 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { llpersonalityslot: Option<ValueRef>, /// A `Block` for each MIR `BasicBlock` - blocks: IndexVec<mir::BasicBlock, Block<'bcx, 'tcx>>, + blocks: IndexVec<mir::BasicBlock, BasicBlockRef>, /// The funclet status of each basic block cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>, /// This stores the landing-pad block for a given BB, computed lazily on GNU /// and eagerly on MSVC. - landing_pads: IndexVec<mir::BasicBlock, Option<Block<'bcx, 'tcx>>>, + landing_pads: IndexVec<mir::BasicBlock, Option<BasicBlockRef>>, /// Cached unreachable block - unreachable_block: Option<Block<'bcx, 'tcx>>, + unreachable_block: Option<BasicBlockRef>, /// The location where each MIR arg/var/tmp/ret is stored. This is /// usually an `LvalueRef` representing an alloca, but not always: @@ -86,18 +95,28 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// Debug information for MIR scopes. scopes: IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>, + + /// If this function is being monomorphized, this contains the type substitutions used. + param_substs: &'tcx Substs<'tcx>, } -impl<'blk, 'tcx> MirContext<'blk, 'tcx> { - pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> DebugLoc { +impl<'a, 'tcx> MirContext<'a, 'tcx> { + pub fn monomorphize<T>(&self, value: &T) -> T + where T: TransNormalize<'tcx> { + monomorphize::apply_param_substs(self.ccx.shared(), self.param_substs, value) + } + + pub fn set_debug_loc(&mut self, bcx: &BlockAndBuilder, source_info: mir::SourceInfo) { + let (scope, span) = self.debug_loc(source_info); + debuginfo::set_source_location(&self.debug_context, bcx, scope, span); + } + + pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> (DIScope, Span) { // Bail out if debug info emission is not enabled. - match self.fcx.debug_context { + match self.debug_context { FunctionDebugContext::DebugInfoDisabled | FunctionDebugContext::FunctionWithoutDebugInfo => { - // Can't return DebugLoc::None here because intrinsic::trans_intrinsic_call() - // relies on debug location to obtain span of the call site. - return DebugLoc::ScopeAt(self.scopes[source_info.scope].scope_metadata, - source_info.span); + return (self.scopes[source_info.scope].scope_metadata, source_info.span); } FunctionDebugContext::RegularContext(_) =>{} } @@ -107,13 +126,12 @@ impl<'blk, 'tcx> MirContext<'blk, 'tcx> { // (unless the crate is being compiled with `-Z debug-macros`). if source_info.span.expn_id == NO_EXPANSION || source_info.span.expn_id == COMMAND_LINE_EXPN || - self.fcx.ccx.sess().opts.debugging_opts.debug_macros { + self.ccx.sess().opts.debugging_opts.debug_macros { - let scope_metadata = self.scope_metadata_for_loc(source_info.scope, - source_info.span.lo); - DebugLoc::ScopeAt(scope_metadata, source_info.span) + let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo); + (scope, source_info.span) } else { - let cm = self.fcx.ccx.sess().codemap(); + let cm = self.ccx.sess().codemap(); // Walk up the macro expansion chain until we reach a non-expanded span. let mut span = source_info.span; while span.expn_id != NO_EXPANSION && span.expn_id != COMMAND_LINE_EXPN { @@ -124,9 +142,9 @@ impl<'blk, 'tcx> MirContext<'blk, 'tcx> { break; } } - let scope_metadata = self.scope_metadata_for_loc(source_info.scope, span.lo); + let scope = self.scope_metadata_for_loc(source_info.scope, span.lo); // Use span of the outermost call site, while keeping the original lexical scope - DebugLoc::ScopeAt(scope_metadata, span) + (scope, span) } } @@ -139,10 +157,8 @@ impl<'blk, 'tcx> MirContext<'blk, 'tcx> { let scope_metadata = self.scopes[scope_id].scope_metadata; if pos < self.scopes[scope_id].file_start_pos || pos >= self.scopes[scope_id].file_end_pos { - let cm = self.fcx.ccx.sess().codemap(); - debuginfo::extend_scope_to_file(self.fcx.ccx, - scope_metadata, - &cm.lookup_char_pos(pos).file) + let cm = self.ccx.sess().codemap(); + debuginfo::extend_scope_to_file(self.ccx, scope_metadata, &cm.lookup_char_pos(pos).file) } else { scope_metadata } @@ -155,7 +171,7 @@ enum LocalRef<'tcx> { } impl<'tcx> LocalRef<'tcx> { - fn new_operand<'bcx>(ccx: &CrateContext<'bcx, 'tcx>, + fn new_operand<'a>(ccx: &CrateContext<'a, 'tcx>, ty: ty::Ty<'tcx>) -> LocalRef<'tcx> { if common::type_is_zero_size(ccx, ty) { // Zero-size temporaries aren't always initialized, which @@ -181,19 +197,22 @@ impl<'tcx> LocalRef<'tcx> { /////////////////////////////////////////////////////////////////////////// -pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { - let bcx = fcx.init(true).build(); - let mir = bcx.mir(); +pub fn trans_mir<'a, 'tcx: 'a>( + fcx: &'a FunctionContext<'a, 'tcx>, + fn_ty: FnType, + mir: &'a Mir<'tcx>, + instance: Instance<'tcx>, + sig: &ty::FnSig<'tcx>, + abi: Abi, +) { + let debug_context = + debuginfo::create_function_debug_context(fcx.ccx, instance, sig, abi, fcx.llfn, mir); + let bcx = fcx.get_entry_block(); - // Analyze the temps to determine which must be lvalues - // FIXME - let (lvalue_locals, cleanup_kinds) = bcx.with_block(|bcx| { - (analyze::lvalue_locals(bcx, &mir), - analyze::cleanup_kinds(bcx, &mir)) - }); + let cleanup_kinds = analyze::cleanup_kinds(&mir); // Allocate a `Block` for every basic block - let block_bcxs: IndexVec<mir::BasicBlock, Block<'blk,'tcx>> = + let block_bcxs: IndexVec<mir::BasicBlock, BasicBlockRef> = mir.basic_blocks().indices().map(|bb| { if bb == mir::START_BLOCK { fcx.new_block("start") @@ -203,11 +222,13 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { }).collect(); // Compute debuginfo scopes from MIR scopes. - let scopes = debuginfo::create_mir_scopes(fcx); + let scopes = debuginfo::create_mir_scopes(fcx, mir, &debug_context); let mut mircx = MirContext { - mir: Ref::clone(&mir), + mir: mir, fcx: fcx, + fn_ty: fn_ty, + ccx: fcx.ccx, llpersonalityslot: None, blocks: block_bcxs, unreachable_block: None, @@ -215,15 +236,22 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { landing_pads: IndexVec::from_elem(None, mir.basic_blocks()), scopes: scopes, locals: IndexVec::new(), + debug_context: debug_context, + param_substs: { + assert!(!instance.substs.needs_infer()); + instance.substs + }, }; + let lvalue_locals = analyze::lvalue_locals(&mircx); + // Allocate variable and temp allocas mircx.locals = { - let args = arg_local_refs(&bcx, &mir, &mircx.scopes, &lvalue_locals); + let args = arg_local_refs(&bcx, &mircx, &mircx.scopes, &lvalue_locals); let mut allocate_local = |local| { let decl = &mir.local_decls[local]; - let ty = bcx.monomorphize(&decl.ty); + let ty = mircx.monomorphize(&decl.ty); if let Some(name) = decl.name { // User variable @@ -233,27 +261,21 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { if !lvalue_locals.contains(local.index()) && !dbg { debug!("alloc: {:?} ({}) -> operand", local, name); - return LocalRef::new_operand(bcx.ccx(), ty); + return LocalRef::new_operand(bcx.ccx, ty); } debug!("alloc: {:?} ({}) -> lvalue", local, name); let lvalue = LvalueRef::alloca(&bcx, ty, &name.as_str()); if dbg { - let dbg_loc = mircx.debug_loc(source_info); - if let DebugLoc::ScopeAt(scope, span) = dbg_loc { - bcx.with_block(|bcx| { - declare_local(bcx, name, ty, scope, - VariableAccess::DirectVariable { alloca: lvalue.llval }, - VariableKind::LocalVariable, span); - }); - } else { - panic!("Unexpected"); - } + let (scope, span) = mircx.debug_loc(source_info); + declare_local(&bcx, &mircx.debug_context, name, ty, scope, + VariableAccess::DirectVariable { alloca: lvalue.llval }, + VariableKind::LocalVariable, span); } LocalRef::Lvalue(lvalue) } else { // Temporary or return pointer - if local == mir::RETURN_POINTER && fcx.fn_ty.ret.is_indirect() { + if local == mir::RETURN_POINTER && mircx.fn_ty.ret.is_indirect() { debug!("alloc: {:?} (return pointer) -> lvalue", local); let llretptr = llvm::get_param(fcx.llfn, 0); LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty))) @@ -265,7 +287,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { // alloca in advance. Instead we wait until we see the // definition and update the operand there. debug!("alloc: {:?} -> operand", local); - LocalRef::new_operand(bcx.ccx(), ty) + LocalRef::new_operand(bcx.ccx, ty) } } }; @@ -279,57 +301,61 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { // Branch to the START block let start_bcx = mircx.blocks[mir::START_BLOCK]; - bcx.br(start_bcx.llbb); + bcx.br(start_bcx); // Up until here, IR instructions for this function have explicitly not been annotated with // source code location, so we don't step into call setup code. From here on, source location // emitting should be enabled. - debuginfo::start_emitting_source_locations(fcx); - - let mut visited = BitVector::new(mir.basic_blocks().len()); + debuginfo::start_emitting_source_locations(&mircx.debug_context); + + let funclets: IndexVec<mir::BasicBlock, Option<Funclet>> = + mircx.cleanup_kinds.iter_enumerated().map(|(bb, cleanup_kind)| { + if let CleanupKind::Funclet = *cleanup_kind { + let bcx = mircx.build_block(bb); + bcx.set_personality_fn(mircx.ccx.eh_personality()); + if base::wants_msvc_seh(fcx.ccx.sess()) { + return Some(Funclet::new(bcx.cleanup_pad(None, &[]))); + } + } - let mut rpo = traversal::reverse_postorder(&mir); + None + }).collect(); - // Prepare each block for translation. - for (bb, _) in rpo.by_ref() { - mircx.init_cpad(bb); - } - rpo.reset(); + let rpo = traversal::reverse_postorder(&mir); + let mut visited = BitVector::new(mir.basic_blocks().len()); // Translate the body of each block using reverse postorder for (bb, _) in rpo { visited.insert(bb.index()); - mircx.trans_block(bb); + mircx.trans_block(bb, &funclets); } // Remove blocks that haven't been visited, or have no // predecessors. for bb in mir.basic_blocks().indices() { - let block = mircx.blocks[bb]; - let block = BasicBlock(block.llbb); // Unreachable block if !visited.contains(bb.index()) { debug!("trans_mir: block {:?} was not visited", bb); - block.delete(); + unsafe { + llvm::LLVMDeleteBasicBlock(mircx.blocks[bb]); + } } } - - DebugLoc::None.apply(fcx); - fcx.cleanup(); } /// Produce, for each argument, a `ValueRef` pointing at the /// argument's value. As arguments are lvalues, these are always /// indirect. -fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, - mir: &mir::Mir<'tcx>, - scopes: &IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>, - lvalue_locals: &BitVector) - -> Vec<LocalRef<'tcx>> { +fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + mircx: &MirContext<'a, 'tcx>, + scopes: &IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>, + lvalue_locals: &BitVector) + -> Vec<LocalRef<'tcx>> { + let mir = mircx.mir; let fcx = bcx.fcx(); let tcx = bcx.tcx(); let mut idx = 0; - let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize; + let mut llarg_idx = mircx.fn_ty.ret.is_indirect() as usize; // Get the argument scope, if it exists and if we need it. let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE]; @@ -341,7 +367,7 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, mir.args_iter().enumerate().map(|(arg_index, local)| { let arg_decl = &mir.local_decls[local]; - let arg_ty = bcx.monomorphize(&arg_decl.ty); + let arg_ty = mircx.monomorphize(&arg_decl.ty); if Some(local) == mir.spread_arg { // This argument (e.g. the last argument in the "rust-call" ABI) @@ -354,22 +380,18 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, _ => bug!("spread argument isn't a tuple?!") }; - let lltemp = bcx.with_block(|bcx| { - base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)) - }); + let lltemp = base::alloc_ty(&bcx, arg_ty, &format!("arg{}", arg_index)); for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() { let dst = bcx.struct_gep(lltemp, i); - let arg = &fcx.fn_ty.args[idx]; + let arg = &mircx.fn_ty.args[idx]; idx += 1; - if common::type_is_fat_ptr(tcx, tupled_arg_ty) { + if common::type_is_fat_ptr(bcx.ccx, tupled_arg_ty) { // We pass fat pointers as two words, but inside the tuple // they are the two sub-fields of a single aggregate field. - let meta = &fcx.fn_ty.args[idx]; + let meta = &mircx.fn_ty.args[idx]; idx += 1; - arg.store_fn_arg(bcx, &mut llarg_idx, - base::get_dataptr_builder(bcx, dst)); - meta.store_fn_arg(bcx, &mut llarg_idx, - base::get_meta_builder(bcx, dst)); + arg.store_fn_arg(bcx, &mut llarg_idx, base::get_dataptr(bcx, dst)); + meta.store_fn_arg(bcx, &mut llarg_idx, base::get_meta(bcx, dst)); } else { arg.store_fn_arg(bcx, &mut llarg_idx, dst); } @@ -377,20 +399,25 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, // Now that we have one alloca that contains the aggregate value, // we can create one debuginfo entry for the argument. - bcx.with_block(|bcx| arg_scope.map(|scope| { + arg_scope.map(|scope| { let variable_access = VariableAccess::DirectVariable { alloca: lltemp }; - declare_local(bcx, arg_decl.name.unwrap_or(keywords::Invalid.name()), - arg_ty, scope, variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - bcx.fcx().span.unwrap_or(DUMMY_SP)); - })); + declare_local( + bcx, + &mircx.debug_context, + arg_decl.name.unwrap_or(keywords::Invalid.name()), + arg_ty, scope, + variable_access, + VariableKind::ArgumentVariable(arg_index + 1), + DUMMY_SP + ); + }); return LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty))); } - let arg = &fcx.fn_ty.args[idx]; + let arg = &mircx.fn_ty.args[idx]; idx += 1; let llval = if arg.is_indirect() && bcx.sess().opts.debuginfo != FullDebugInfo { // Don't copy an indirect argument to an alloca, the caller @@ -407,7 +434,7 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, !arg.is_indirect() && arg.cast.is_none() && arg_scope.is_none() { if arg.is_ignore() { - return LocalRef::new_operand(bcx.ccx(), arg_ty); + return LocalRef::new_operand(bcx.ccx, arg_ty); } // We don't have to cast or keep the argument in the alloca. @@ -418,8 +445,8 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, } let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); llarg_idx += 1; - let val = if common::type_is_fat_ptr(tcx, arg_ty) { - let meta = &fcx.fn_ty.args[idx]; + let val = if common::type_is_fat_ptr(bcx.ccx, arg_ty) { + let meta = &mircx.fn_ty.args[idx]; idx += 1; assert_eq!((meta.cast, meta.pad), (None, None)); let llmeta = llvm::get_param(fcx.llfn, llarg_idx as c_uint); @@ -434,19 +461,15 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, }; return LocalRef::Operand(Some(operand.unpack_if_pair(bcx))); } else { - let lltemp = bcx.with_block(|bcx| { - base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)) - }); - if common::type_is_fat_ptr(tcx, arg_ty) { + let lltemp = base::alloc_ty(&bcx, arg_ty, &format!("arg{}", arg_index)); + if common::type_is_fat_ptr(bcx.ccx, arg_ty) { // we pass fat pointers as two words, but we want to // represent them internally as a pointer to two words, // so make an alloca to store them in. - let meta = &fcx.fn_ty.args[idx]; + let meta = &mircx.fn_ty.args[idx]; idx += 1; - arg.store_fn_arg(bcx, &mut llarg_idx, - base::get_dataptr_builder(bcx, lltemp)); - meta.store_fn_arg(bcx, &mut llarg_idx, - base::get_meta_builder(bcx, lltemp)); + arg.store_fn_arg(bcx, &mut llarg_idx, base::get_dataptr(bcx, lltemp)); + meta.store_fn_arg(bcx, &mut llarg_idx, base::get_meta(bcx, lltemp)); } else { // otherwise, arg is passed by value, so make a // temporary and store it there @@ -454,13 +477,19 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, } lltemp }; - bcx.with_block(|bcx| arg_scope.map(|scope| { + arg_scope.map(|scope| { // Is this a regular argument? if arg_index > 0 || mir.upvar_decls.is_empty() { - declare_local(bcx, arg_decl.name.unwrap_or(keywords::Invalid.name()), arg_ty, - scope, VariableAccess::DirectVariable { alloca: llval }, - VariableKind::ArgumentVariable(arg_index + 1), - bcx.fcx().span.unwrap_or(DUMMY_SP)); + declare_local( + bcx, + &mircx.debug_context, + arg_decl.name.unwrap_or(keywords::Invalid.name()), + arg_ty, + scope, + VariableAccess::DirectVariable { alloca: llval }, + VariableKind::ArgumentVariable(arg_index + 1), + DUMMY_SP + ); return; } @@ -484,20 +513,21 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, // doesn't actually strip the offset when splitting the closure // environment into its components so it ends up out of bounds. let env_ptr = if !env_ref { - use base::*; - use build::*; - use common::*; - let alloc = alloca(bcx, val_ty(llval), "__debuginfo_env_ptr"); - Store(bcx, llval, alloc); + let alloc = bcx.fcx().alloca(common::val_ty(llval), "__debuginfo_env_ptr"); + bcx.store(llval, alloc); alloc } else { llval }; - let llclosurety = type_of::type_of(bcx.ccx(), closure_ty); + let layout = bcx.ccx.layout_of(closure_ty); + let offsets = match *layout { + layout::Univariant { ref variant, .. } => &variant.offsets[..], + _ => bug!("Closures are only supposed to be Univariant") + }; + for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() { - let byte_offset_of_var_in_env = - machine::llelement_offset(bcx.ccx(), llclosurety, i); + let byte_offset_of_var_in_env = offsets[i].bytes(); let ops = unsafe { [llvm::LLVMRustDIBuilderCreateOpDeref(), @@ -523,11 +553,18 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, alloca: env_ptr, address_operations: &ops }; - declare_local(bcx, decl.debug_name, ty, scope, variable_access, - VariableKind::CapturedVariable, - bcx.fcx().span.unwrap_or(DUMMY_SP)); + declare_local( + bcx, + &mircx.debug_context, + decl.debug_name, + ty, + scope, + variable_access, + VariableKind::CapturedVariable, + DUMMY_SP + ); } - })); + }); LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))) }).collect() } diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 62eda56e2e1..a15d51d9da6 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -14,7 +14,7 @@ use rustc::mir; use rustc_data_structures::indexed_vec::Idx; use base; -use common::{self, Block, BlockAndBuilder}; +use common::{self, BlockAndBuilder}; use value::Value; use type_of; use type_::Type; @@ -73,7 +73,7 @@ impl<'tcx> fmt::Debug for OperandRef<'tcx> { } } -impl<'bcx, 'tcx> OperandRef<'tcx> { +impl<'a, 'tcx> OperandRef<'tcx> { /// Asserts that this operand refers to a scalar and returns /// a reference to its value. pub fn immediate(self) -> ValueRef { @@ -85,18 +85,18 @@ impl<'bcx, 'tcx> OperandRef<'tcx> { /// If this operand is a Pair, we return an /// Immediate aggregate with the two values. - pub fn pack_if_pair(mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>) + pub fn pack_if_pair(mut self, bcx: &BlockAndBuilder<'a, 'tcx>) -> OperandRef<'tcx> { if let OperandValue::Pair(a, b) = self.val { // Reconstruct the immediate aggregate. - let llty = type_of::type_of(bcx.ccx(), self.ty); + let llty = type_of::type_of(bcx.ccx, self.ty); let mut llpair = common::C_undef(llty); let elems = [a, b]; for i in 0..2 { let mut elem = elems[i]; // Extend boolean i1's to i8. - if common::val_ty(elem) == Type::i1(bcx.ccx()) { - elem = bcx.zext(elem, Type::i8(bcx.ccx())); + if common::val_ty(elem) == Type::i1(bcx.ccx) { + elem = bcx.zext(elem, Type::i8(bcx.ccx)); } llpair = bcx.insert_value(llpair, elem, i); } @@ -107,23 +107,23 @@ impl<'bcx, 'tcx> OperandRef<'tcx> { /// If this operand is a pair in an Immediate, /// we return a Pair with the two halves. - pub fn unpack_if_pair(mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>) + pub fn unpack_if_pair(mut self, bcx: &BlockAndBuilder<'a, 'tcx>) -> OperandRef<'tcx> { if let OperandValue::Immediate(llval) = self.val { // Deconstruct the immediate aggregate. - if common::type_is_imm_pair(bcx.ccx(), self.ty) { + if common::type_is_imm_pair(bcx.ccx, self.ty) { debug!("Operand::unpack_if_pair: unpacking {:?}", self); let mut a = bcx.extract_value(llval, 0); let mut b = bcx.extract_value(llval, 1); - let pair_fields = common::type_pair_fields(bcx.ccx(), self.ty); + let pair_fields = common::type_pair_fields(bcx.ccx, self.ty); if let Some([a_ty, b_ty]) = pair_fields { if a_ty.is_bool() { - a = bcx.trunc(a, Type::i1(bcx.ccx())); + a = bcx.trunc(a, Type::i1(bcx.ccx)); } if b_ty.is_bool() { - b = bcx.trunc(b, Type::i1(bcx.ccx())); + b = bcx.trunc(b, Type::i1(bcx.ccx)); } } @@ -134,29 +134,29 @@ impl<'bcx, 'tcx> OperandRef<'tcx> { } } -impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { +impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_load(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, llval: ValueRef, ty: Ty<'tcx>) -> OperandRef<'tcx> { debug!("trans_load: {:?} @ {:?}", Value(llval), ty); - let val = if common::type_is_fat_ptr(bcx.tcx(), ty) { - let (lldata, llextra) = base::load_fat_ptr_builder(bcx, llval, ty); + let val = if common::type_is_fat_ptr(bcx.ccx, ty) { + let (lldata, llextra) = base::load_fat_ptr(bcx, llval, ty); OperandValue::Pair(lldata, llextra) - } else if common::type_is_imm_pair(bcx.ccx(), ty) { - let [a_ty, b_ty] = common::type_pair_fields(bcx.ccx(), ty).unwrap(); + } else if common::type_is_imm_pair(bcx.ccx, ty) { + let [a_ty, b_ty] = common::type_pair_fields(bcx.ccx, ty).unwrap(); let a_ptr = bcx.struct_gep(llval, 0); let b_ptr = bcx.struct_gep(llval, 1); OperandValue::Pair( - base::load_ty_builder(bcx, a_ptr, a_ty), - base::load_ty_builder(bcx, b_ptr, b_ty) + base::load_ty(bcx, a_ptr, a_ty), + base::load_ty(bcx, b_ptr, b_ty) ) - } else if common::type_is_immediate(bcx.ccx(), ty) { - OperandValue::Immediate(base::load_ty_builder(bcx, llval, ty)) + } else if common::type_is_immediate(bcx.ccx, ty) { + OperandValue::Immediate(base::load_ty(bcx, llval, ty)) } else { OperandValue::Ref(llval) }; @@ -165,7 +165,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } pub fn trans_consume(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, lvalue: &mir::Lvalue<'tcx>) -> OperandRef<'tcx> { @@ -197,7 +197,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let llval = [a, b][f.index()]; let op = OperandRef { val: OperandValue::Immediate(llval), - ty: bcx.monomorphize(&ty) + ty: self.monomorphize(&ty) }; // Handle nested pairs. @@ -217,7 +217,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } pub fn trans_operand(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, operand: &mir::Operand<'tcx>) -> OperandRef<'tcx> { @@ -230,7 +230,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Operand::Constant(ref constant) => { let val = self.trans_constant(bcx, constant); - let operand = val.to_operand(bcx.ccx()); + let operand = val.to_operand(bcx.ccx); if let OperandValue::Ref(ptr) = operand.val { // If this is a OperandValue::Ref to an immediate constant, load it. self.trans_load(bcx, ptr, operand.ty) @@ -242,33 +242,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } pub fn store_operand(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, lldest: ValueRef, - operand: OperandRef<'tcx>) - { + operand: OperandRef<'tcx>) { debug!("store_operand: operand={:?}", operand); - bcx.with_block(|bcx| self.store_operand_direct(bcx, lldest, operand)) - } - - pub fn store_operand_direct(&mut self, - bcx: Block<'bcx, 'tcx>, - lldest: ValueRef, - operand: OperandRef<'tcx>) - { // Avoid generating stores of zero-sized values, because the only way to have a zero-sized // value is through `undef`, and store itself is useless. - if common::type_is_zero_size(bcx.ccx(), operand.ty) { + if common::type_is_zero_size(bcx.ccx, operand.ty) { return; } match operand.val { OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty), OperandValue::Immediate(s) => base::store_ty(bcx, s, lldest, operand.ty), OperandValue::Pair(a, b) => { - use build::*; let a = base::from_immediate(bcx, a); let b = base::from_immediate(bcx, b); - Store(bcx, a, StructGEP(bcx, lldest, 0)); - Store(bcx, b, StructGEP(bcx, lldest, 1)); + bcx.store(a, bcx.struct_gep(lldest, 0)); + bcx.store(b, bcx.struct_gep(lldest, 1)); } } } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 15cbbc720d6..b17550087ed 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -13,13 +13,13 @@ use rustc::ty::{self, Ty}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::layout::Layout; use rustc::mir; +use middle::lang_items::ExchangeMallocFnLangItem; use asm; use base; use callee::Callee; -use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder, Result}; +use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder}; use common::{C_integral}; -use debuginfo::DebugLoc; use adt; use machine; use type_::Type; @@ -33,13 +33,12 @@ use super::constant::const_scalar_checked_binop; use super::operand::{OperandRef, OperandValue}; use super::lvalue::{LvalueRef}; -impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { +impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_rvalue(&mut self, - bcx: BlockAndBuilder<'bcx, 'tcx>, + bcx: BlockAndBuilder<'a, 'tcx>, dest: LvalueRef<'tcx>, - rvalue: &mir::Rvalue<'tcx>, - debug_loc: DebugLoc) - -> BlockAndBuilder<'bcx, 'tcx> + rvalue: &mir::Rvalue<'tcx>) + -> BlockAndBuilder<'a, 'tcx> { debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})", Value(dest.llval), rvalue); @@ -54,12 +53,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, cast_ty) => { - let cast_ty = bcx.monomorphize(&cast_ty); + let cast_ty = self.monomorphize(&cast_ty); - if common::type_is_fat_ptr(bcx.tcx(), cast_ty) { + if common::type_is_fat_ptr(bcx.ccx, cast_ty) { // into-coerce of a thin pointer to a fat pointer - just // use the operand path. - let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue, debug_loc); + let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); self.store_operand(&bcx, dest.llval, temp); return bcx; } @@ -70,76 +69,74 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // so the (generic) MIR may not be able to expand it. let operand = self.trans_operand(&bcx, source); let operand = operand.pack_if_pair(&bcx); - bcx.with_block(|bcx| { - match operand.val { - OperandValue::Pair(..) => bug!(), - OperandValue::Immediate(llval) => { - // unsize from an immediate structure. We don't - // really need a temporary alloca here, but - // avoiding it would require us to have - // `coerce_unsized_into` use extractvalue to - // index into the struct, and this case isn't - // important enough for it. - debug!("trans_rvalue: creating ugly alloca"); - let lltemp = base::alloc_ty(bcx, operand.ty, "__unsize_temp"); - base::store_ty(bcx, llval, lltemp, operand.ty); - base::coerce_unsized_into(bcx, - lltemp, operand.ty, - dest.llval, cast_ty); - } - OperandValue::Ref(llref) => { - base::coerce_unsized_into(bcx, - llref, operand.ty, - dest.llval, cast_ty); - } + let llref = match operand.val { + OperandValue::Pair(..) => bug!(), + OperandValue::Immediate(llval) => { + // unsize from an immediate structure. We don't + // really need a temporary alloca here, but + // avoiding it would require us to have + // `coerce_unsized_into` use extractvalue to + // index into the struct, and this case isn't + // important enough for it. + debug!("trans_rvalue: creating ugly alloca"); + let lltemp = base::alloc_ty(&bcx, operand.ty, "__unsize_temp"); + base::store_ty(&bcx, llval, lltemp, operand.ty); + lltemp } - }); + OperandValue::Ref(llref) => llref + }; + base::coerce_unsized_into(&bcx, llref, operand.ty, dest.llval, cast_ty); bcx } mir::Rvalue::Repeat(ref elem, ref count) => { let tr_elem = self.trans_operand(&bcx, elem); let size = count.value.as_u64(bcx.tcx().sess.target.uint_type); - let size = C_uint(bcx.ccx(), size); - let base = base::get_dataptr_builder(&bcx, dest.llval); - let bcx = bcx.map_block(|block| { - tvec::slice_for_each(block, base, tr_elem.ty, size, |block, llslot| { - self.store_operand_direct(block, llslot, tr_elem); - block - }) - }); - bcx + let size = C_uint(bcx.ccx, size); + let base = base::get_dataptr(&bcx, dest.llval); + tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot| { + self.store_operand(bcx, llslot, tr_elem); + }) } mir::Rvalue::Aggregate(ref kind, ref operands) => { match *kind { mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { let disr = Disr::from(adt_def.variants[variant_index].disr_val); - bcx.with_block(|bcx| { - adt::trans_set_discr(bcx, - dest.ty.to_ty(bcx.tcx()), dest.llval, Disr::from(disr)); - }); + let dest_ty = dest.ty.to_ty(bcx.tcx()); + adt::trans_set_discr(&bcx, dest_ty, dest.llval, Disr::from(disr)); for (i, operand) in operands.iter().enumerate() { let op = self.trans_operand(&bcx, operand); // Do not generate stores and GEPis for zero-sized fields. - if !common::type_is_zero_size(bcx.ccx(), op.ty) { + if !common::type_is_zero_size(bcx.ccx, op.ty) { let val = adt::MaybeSizedValue::sized(dest.llval); let field_index = active_field_index.unwrap_or(i); - let lldest_i = adt::trans_field_ptr_builder(&bcx, - dest.ty.to_ty(bcx.tcx()), - val, disr, field_index); + let lldest_i = adt::trans_field_ptr(&bcx, dest_ty, val, disr, + field_index); self.store_operand(&bcx, lldest_i, op); } } }, _ => { + // If this is a tuple or closure, we need to translate GEP indices. + let layout = bcx.ccx.layout_of(dest.ty.to_ty(bcx.tcx())); + let translation = if let Layout::Univariant { ref variant, .. } = *layout { + Some(&variant.memory_index) + } else { + None + }; for (i, operand) in operands.iter().enumerate() { let op = self.trans_operand(&bcx, operand); // Do not generate stores and GEPis for zero-sized fields. - if !common::type_is_zero_size(bcx.ccx(), op.ty) { + if !common::type_is_zero_size(bcx.ccx, op.ty) { // Note: perhaps this should be StructGep, but // note that in some cases the values here will // not be structs but arrays. + let i = if let Some(ref t) = translation { + t[i] as usize + } else { + i + }; let dest = bcx.gepi(dest.llval, &[0, i]); self.store_operand(&bcx, dest, op); } @@ -159,16 +156,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.trans_operand(&bcx, input).immediate() }).collect(); - bcx.with_block(|bcx| { - asm::trans_inline_asm(bcx, asm, outputs, input_vals); - }); - + asm::trans_inline_asm(&bcx, asm, outputs, input_vals); bcx } _ => { - assert!(rvalue_creates_operand(&self.mir, &bcx, rvalue)); - let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue, debug_loc); + assert!(rvalue_creates_operand(rvalue)); + let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); self.store_operand(&bcx, dest.llval, temp); bcx } @@ -176,27 +170,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } pub fn trans_rvalue_operand(&mut self, - bcx: BlockAndBuilder<'bcx, 'tcx>, - rvalue: &mir::Rvalue<'tcx>, - debug_loc: DebugLoc) - -> (BlockAndBuilder<'bcx, 'tcx>, OperandRef<'tcx>) + bcx: BlockAndBuilder<'a, 'tcx>, + rvalue: &mir::Rvalue<'tcx>) + -> (BlockAndBuilder<'a, 'tcx>, OperandRef<'tcx>) { - assert!(rvalue_creates_operand(&self.mir, &bcx, rvalue), - "cannot trans {:?} to operand", rvalue); + assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); match *rvalue { mir::Rvalue::Cast(ref kind, ref source, cast_ty) => { let operand = self.trans_operand(&bcx, source); debug!("cast operand is {:?}", operand); - let cast_ty = bcx.monomorphize(&cast_ty); + let cast_ty = self.monomorphize(&cast_ty); let val = match *kind { mir::CastKind::ReifyFnPointer => { match operand.ty.sty { ty::TyFnDef(def_id, substs, _) => { OperandValue::Immediate( - Callee::def(bcx.ccx(), def_id, substs) - .reify(bcx.ccx())) + Callee::def(bcx.ccx, def_id, substs) + .reify(bcx.ccx)) } _ => { bug!("{} cannot be reified to a fn ptr", operand.ty) @@ -210,7 +202,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::CastKind::Unsize => { // unsize targets other than to a fat pointer currently // can't be operands. - assert!(common::type_is_fat_ptr(bcx.tcx(), cast_ty)); + assert!(common::type_is_fat_ptr(bcx.ccx, cast_ty)); match operand.val { OperandValue::Pair(lldata, llextra) => { @@ -220,16 +212,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // &'a fmt::Debug+Send => &'a fmt::Debug, // So we need to pointercast the base to ensure // the types match up. - let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx(), cast_ty); + let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx, cast_ty); let lldata = bcx.pointercast(lldata, llcast_ty); OperandValue::Pair(lldata, llextra) } OperandValue::Immediate(lldata) => { // "standard" unsize - let (lldata, llextra) = bcx.with_block(|bcx| { - base::unsize_thin_ptr(bcx, lldata, - operand.ty, cast_ty) - }); + let (lldata, llextra) = base::unsize_thin_ptr(&bcx, lldata, + operand.ty, cast_ty); OperandValue::Pair(lldata, llextra) } OperandValue::Ref(_) => { @@ -238,11 +228,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } } - mir::CastKind::Misc if common::type_is_fat_ptr(bcx.tcx(), operand.ty) => { - let ll_cast_ty = type_of::immediate_type_of(bcx.ccx(), cast_ty); - let ll_from_ty = type_of::immediate_type_of(bcx.ccx(), operand.ty); + mir::CastKind::Misc if common::type_is_fat_ptr(bcx.ccx, operand.ty) => { + let ll_cast_ty = type_of::immediate_type_of(bcx.ccx, cast_ty); + let ll_from_ty = type_of::immediate_type_of(bcx.ccx, operand.ty); if let OperandValue::Pair(data_ptr, meta_ptr) = operand.val { - if common::type_is_fat_ptr(bcx.tcx(), cast_ty) { + if common::type_is_fat_ptr(bcx.ccx, cast_ty) { let ll_cft = ll_cast_ty.field_types(); let ll_fft = ll_from_ty.field_types(); let data_cast = bcx.pointercast(data_ptr, ll_cft[0]); @@ -259,19 +249,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } mir::CastKind::Misc => { - debug_assert!(common::type_is_immediate(bcx.ccx(), cast_ty)); + debug_assert!(common::type_is_immediate(bcx.ccx, cast_ty)); let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast"); let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - let ll_t_in = type_of::immediate_type_of(bcx.ccx(), operand.ty); - let ll_t_out = type_of::immediate_type_of(bcx.ccx(), cast_ty); + let ll_t_in = type_of::immediate_type_of(bcx.ccx, operand.ty); + let ll_t_out = type_of::immediate_type_of(bcx.ccx, cast_ty); let (llval, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in { - let l = bcx.ccx().layout_of(operand.ty); + let l = bcx.ccx.layout_of(operand.ty); let discr = match operand.val { OperandValue::Immediate(llval) => llval, OperandValue::Ref(llptr) => { - bcx.with_block(|bcx| { - adt::trans_get_discr(bcx, operand.ty, llptr, None, true) - }) + adt::trans_get_discr(&bcx, operand.ty, llptr, None, true) } OperandValue::Pair(..) => bug!("Unexpected Pair operand") }; @@ -364,7 +352,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // Note: lvalues are indirect, so storing the `llval` into the // destination effectively creates a reference. - let operand = if common::type_is_sized(bcx.tcx(), ty) { + let operand = if bcx.ccx.shared().type_is_sized(ty) { OperandRef { val: OperandValue::Immediate(tr_lvalue.llval), ty: ref_ty, @@ -382,7 +370,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::Len(ref lvalue) => { let tr_lvalue = self.trans_lvalue(&bcx, lvalue); let operand = OperandRef { - val: OperandValue::Immediate(tr_lvalue.len(bcx.ccx())), + val: OperandValue::Immediate(tr_lvalue.len(bcx.ccx)), ty: bcx.tcx().types.usize, }; (bcx, operand) @@ -391,7 +379,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { let lhs = self.trans_operand(&bcx, lhs); let rhs = self.trans_operand(&bcx, rhs); - let llresult = if common::type_is_fat_ptr(bcx.tcx(), lhs.ty) { + let llresult = if common::type_is_fat_ptr(bcx.ccx, lhs.ty) { match (lhs.val, rhs.val) { (OperandValue::Pair(lhs_addr, lhs_extra), OperandValue::Pair(rhs_addr, rhs_extra)) => { @@ -449,26 +437,27 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Rvalue::Box(content_ty) => { - let content_ty: Ty<'tcx> = bcx.monomorphize(&content_ty); - let llty = type_of::type_of(bcx.ccx(), content_ty); - let llsize = machine::llsize_of(bcx.ccx(), llty); - let align = type_of::align_of(bcx.ccx(), content_ty); - let llalign = C_uint(bcx.ccx(), align); + let content_ty: Ty<'tcx> = self.monomorphize(&content_ty); + let llty = type_of::type_of(bcx.ccx, content_ty); + let llsize = machine::llsize_of(bcx.ccx, llty); + let align = type_of::align_of(bcx.ccx, content_ty); + let llalign = C_uint(bcx.ccx, align); let llty_ptr = llty.ptr_to(); let box_ty = bcx.tcx().mk_box(content_ty); - let mut llval = None; - let bcx = bcx.map_block(|bcx| { - let Result { bcx, val } = base::malloc_raw_dyn(bcx, - llty_ptr, - box_ty, - llsize, - llalign, - debug_loc); - llval = Some(val); - bcx - }); + + // Allocate space: + let def_id = match bcx.tcx().lang_items.require(ExchangeMallocFnLangItem) { + Ok(id) => id, + Err(s) => { + bcx.sess().fatal(&format!("allocation of `{}` {}", box_ty, s)); + } + }; + let r = Callee::def(bcx.ccx, def_id, bcx.tcx().intern_substs(&[])) + .reify(bcx.ccx); + let val = bcx.pointercast(bcx.call(r, &[llsize, llalign], None), llty_ptr); + let operand = OperandRef { - val: OperandValue::Immediate(llval.unwrap()), + val: OperandValue::Immediate(val), ty: box_ty, }; (bcx, operand) @@ -488,7 +477,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } pub fn trans_scalar_binop(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, op: mir::BinOp, lhs: ValueRef, rhs: ValueRef, @@ -530,26 +519,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::BinOp::BitOr => bcx.or(lhs, rhs), mir::BinOp::BitAnd => bcx.and(lhs, rhs), mir::BinOp::BitXor => bcx.xor(lhs, rhs), - mir::BinOp::Shl => { - bcx.with_block(|bcx| { - common::build_unchecked_lshift(bcx, - lhs, - rhs, - DebugLoc::None) - }) - } - mir::BinOp::Shr => { - bcx.with_block(|bcx| { - common::build_unchecked_rshift(bcx, - input_ty, - lhs, - rhs, - DebugLoc::None) - }) - } + mir::BinOp::Shl => common::build_unchecked_lshift(bcx, lhs, rhs), + mir::BinOp::Shr => common::build_unchecked_rshift(bcx, input_ty, lhs, rhs), mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt | mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_nil { - C_bool(bcx.ccx(), match op { + C_bool(bcx.ccx, match op { mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false, mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true, _ => unreachable!() @@ -563,8 +537,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let (lhs, rhs) = if is_bool { // FIXME(#36856) -- extend the bools into `i8` because // LLVM's i1 comparisons are broken. - (bcx.zext(lhs, Type::i8(bcx.ccx())), - bcx.zext(rhs, Type::i8(bcx.ccx()))) + (bcx.zext(lhs, Type::i8(bcx.ccx)), + bcx.zext(rhs, Type::i8(bcx.ccx))) } else { (lhs, rhs) }; @@ -578,7 +552,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } pub fn trans_fat_ptr_binop(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, op: mir::BinOp, lhs_addr: ValueRef, lhs_extra: ValueRef, @@ -625,7 +599,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } pub fn trans_scalar_checked_binop(&mut self, - bcx: &BlockAndBuilder<'bcx, 'tcx>, + bcx: &BlockAndBuilder<'a, 'tcx>, op: mir::BinOp, lhs: ValueRef, rhs: ValueRef, @@ -634,9 +608,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // with #[rustc_inherit_overflow_checks] and inlined from // another crate (mostly core::num generic/#[inline] fns), // while the current crate doesn't use overflow checks. - if !bcx.ccx().check_overflow() { + if !bcx.ccx.check_overflow() { let val = self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty); - return OperandValue::Pair(val, C_bool(bcx.ccx(), false)); + return OperandValue::Pair(val, C_bool(bcx.ccx, false)); } // First try performing the operation on constants, which @@ -644,7 +618,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // This is necessary to determine when an overflow Assert // will always panic at runtime, and produce a warning. if let Some((val, of)) = const_scalar_checked_binop(bcx.tcx(), op, lhs, rhs, input_ty) { - return OperandValue::Pair(val, C_bool(bcx.ccx(), of)); + return OperandValue::Pair(val, C_bool(bcx.ccx, of)); } let (val, of) = match op { @@ -665,9 +639,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::BinOp::Shl | mir::BinOp::Shr => { let lhs_llty = val_ty(lhs); let rhs_llty = val_ty(rhs); - let invert_mask = bcx.with_block(|bcx| { - common::shift_mask_val(bcx, lhs_llty, rhs_llty, true) - }); + let invert_mask = common::shift_mask_val(&bcx, lhs_llty, rhs_llty, true); let outer_bits = bcx.and(rhs, invert_mask); let of = bcx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty)); @@ -684,9 +656,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } -pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>, - _bcx: &BlockAndBuilder<'bcx, 'tcx>, - rvalue: &mir::Rvalue<'tcx>) -> bool { +pub fn rvalue_creates_operand(rvalue: &mir::Rvalue) -> bool { match *rvalue { mir::Rvalue::Ref(..) | mir::Rvalue::Len(..) | @@ -777,5 +747,5 @@ fn get_overflow_intrinsic(oop: OverflowOp, bcx: &BlockAndBuilder, ty: Ty) -> Val }, }; - bcx.ccx().get_intrinsic(&name) + bcx.ccx.get_intrinsic(&name) } diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index 296a0e8049e..cc85f68c197 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -18,57 +18,52 @@ use super::LocalRef; use super::super::adt; use super::super::disr::Disr; -impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { +impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_statement(&mut self, - bcx: BlockAndBuilder<'bcx, 'tcx>, + bcx: BlockAndBuilder<'a, 'tcx>, statement: &mir::Statement<'tcx>) - -> BlockAndBuilder<'bcx, 'tcx> { + -> BlockAndBuilder<'a, 'tcx> { debug!("trans_statement(statement={:?})", statement); - let debug_loc = self.debug_loc(statement.source_info); - debug_loc.apply_to_bcx(&bcx); - debug_loc.apply(bcx.fcx()); + self.set_debug_loc(&bcx, statement.source_info); match statement.kind { mir::StatementKind::Assign(ref lvalue, ref rvalue) => { if let mir::Lvalue::Local(index) = *lvalue { match self.locals[index] { LocalRef::Lvalue(tr_dest) => { - self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) + self.trans_rvalue(bcx, tr_dest, rvalue) } LocalRef::Operand(None) => { - let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue, - debug_loc); + let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue); self.locals[index] = LocalRef::Operand(Some(operand)); bcx } LocalRef::Operand(Some(_)) => { let ty = self.monomorphized_lvalue_ty(lvalue); - if !common::type_is_zero_size(bcx.ccx(), ty) { + if !common::type_is_zero_size(bcx.ccx, ty) { span_bug!(statement.source_info.span, "operand {:?} already assigned", rvalue); } else { // If the type is zero-sized, it's already been set here, // but we still need to make sure we translate the operand - self.trans_rvalue_operand(bcx, rvalue, debug_loc).0 + self.trans_rvalue_operand(bcx, rvalue).0 } } } } else { let tr_dest = self.trans_lvalue(&bcx, lvalue); - self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) + self.trans_rvalue(bcx, tr_dest, rvalue) } } mir::StatementKind::SetDiscriminant{ref lvalue, variant_index} => { let ty = self.monomorphized_lvalue_ty(lvalue); let lvalue_transed = self.trans_lvalue(&bcx, lvalue); - bcx.with_block(|bcx| - adt::trans_set_discr(bcx, - ty, - lvalue_transed.llval, - Disr::from(variant_index)) - ); + adt::trans_set_discr(&bcx, + ty, + lvalue_transed.llval, + Disr::from(variant_index)); bcx } mir::StatementKind::StorageLive(ref lvalue) => { @@ -82,10 +77,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } fn trans_storage_liveness(&self, - bcx: BlockAndBuilder<'bcx, 'tcx>, + bcx: BlockAndBuilder<'a, 'tcx>, lvalue: &mir::Lvalue<'tcx>, intrinsic: base::Lifetime) - -> BlockAndBuilder<'bcx, 'tcx> { + -> BlockAndBuilder<'a, 'tcx> { if let mir::Lvalue::Local(index) = *lvalue { if let LocalRef::Lvalue(tr_lval) = self.locals[index] { intrinsic.call(&bcx, tr_lval.llval); diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 214eaeb817f..6e0d8d08e70 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -184,15 +184,14 @@ impl<'a, 'tcx> TransItem<'tcx> { linkage: llvm::Linkage, symbol_name: &str) { let tcx = ccx.tcx(); - assert_eq!(dg.ty(), glue::get_drop_glue_type(tcx, dg.ty())); + assert_eq!(dg.ty(), glue::get_drop_glue_type(ccx.shared(), dg.ty())); let t = dg.ty(); - let sig = tcx.mk_fn_sig(iter::once(tcx.mk_mut_ptr(tcx.types.i8)), tcx.mk_nil(), false); + let sig = tcx.mk_fn_sig(iter::once(tcx.mk_mut_ptr(t)), tcx.mk_nil(), false); - // Create a FnType for fn(*mut i8) and substitute the real type in - // later - that prevents FnType from splitting fat pointers up. - let mut fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]); - fn_ty.args[0].original_ty = type_of::type_of(ccx, t).ptr_to(); + debug!("predefine_drop_glue: sig={}", sig); + + let fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]); let llfnty = fn_ty.llvm_type(ccx); assert!(declare::get_defined_value(ccx, symbol_name).is_none()); diff --git a/src/librustc_trans/tvec.rs b/src/librustc_trans/tvec.rs index cf897fc5a15..c09726fda08 100644 --- a/src/librustc_trans/tvec.rs +++ b/src/librustc_trans/tvec.rs @@ -8,56 +8,46 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(non_camel_case_types)] - use llvm; use llvm::ValueRef; -use base::*; -use build::*; use common::*; -use debuginfo::DebugLoc; use rustc::ty::Ty; -pub fn slice_for_each<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, - data_ptr: ValueRef, - unit_ty: Ty<'tcx>, - len: ValueRef, - f: F) - -> Block<'blk, 'tcx> where - F: FnOnce(Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>, -{ - let _icx = push_ctxt("tvec::slice_for_each"); - let fcx = bcx.fcx; - +pub fn slice_for_each<'a, 'tcx, F>( + bcx: &BlockAndBuilder<'a, 'tcx>, + data_ptr: ValueRef, + unit_ty: Ty<'tcx>, + len: ValueRef, + f: F +) -> BlockAndBuilder<'a, 'tcx> where F: FnOnce(&BlockAndBuilder<'a, 'tcx>, ValueRef) { // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890) - let zst = type_is_zero_size(bcx.ccx(), unit_ty); - let add = |bcx, a, b| if zst { - Add(bcx, a, b, DebugLoc::None) + let zst = type_is_zero_size(bcx.ccx, unit_ty); + let add = |bcx: &BlockAndBuilder, a, b| if zst { + bcx.add(a, b) } else { - InBoundsGEP(bcx, a, &[b]) + bcx.inbounds_gep(a, &[b]) }; - let header_bcx = fcx.new_block("slice_loop_header"); - let body_bcx = fcx.new_block("slice_loop_body"); - let next_bcx = fcx.new_block("slice_loop_next"); + let body_bcx = bcx.fcx().build_new_block("slice_loop_body"); + let next_bcx = bcx.fcx().build_new_block("slice_loop_next"); + let header_bcx = bcx.fcx().build_new_block("slice_loop_header"); let start = if zst { - C_uint(bcx.ccx(), 0 as usize) + C_uint(bcx.ccx, 0usize) } else { data_ptr }; - let end = add(bcx, start, len); + let end = add(&bcx, start, len); - Br(bcx, header_bcx.llbb, DebugLoc::None); - let current = Phi(header_bcx, val_ty(start), &[start], &[bcx.llbb]); + bcx.br(header_bcx.llbb()); + let current = header_bcx.phi(val_ty(start), &[start], &[bcx.llbb()]); - let keep_going = - ICmp(header_bcx, llvm::IntNE, current, end, DebugLoc::None); - CondBr(header_bcx, keep_going, body_bcx.llbb, next_bcx.llbb, DebugLoc::None); + let keep_going = header_bcx.icmp(llvm::IntNE, current, end); + header_bcx.cond_br(keep_going, body_bcx.llbb(), next_bcx.llbb()); - let body_bcx = f(body_bcx, if zst { data_ptr } else { current }); - let next = add(body_bcx, current, C_uint(bcx.ccx(), 1usize)); - AddIncomingToPhi(current, next, body_bcx.llbb); - Br(body_bcx, header_bcx.llbb, DebugLoc::None); + f(&body_bcx, if zst { data_ptr } else { current }); + let next = add(&body_bcx, current, C_uint(bcx.ccx, 1usize)); + header_bcx.add_incoming_to_phi(current, next, body_bcx.llbb()); + body_bcx.br(header_bcx.llbb()); next_bcx } diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs index 22c405fe254..469214b466e 100644 --- a/src/librustc_trans/type_of.rs +++ b/src/librustc_trans/type_of.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(non_camel_case_types)] - use abi::FnType; use adt; use common::*; @@ -41,7 +39,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ let _recursion_lock = cx.enter_type_of(t); let llsizingty = match t.sty { - _ if !type_is_sized(cx.tcx(), t) => { + _ if !cx.shared().type_is_sized(t) => { Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, t)], false) } @@ -55,7 +53,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ ty::TyBox(ty) | ty::TyRef(_, ty::TypeAndMut{ty, ..}) | ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => { - if type_is_sized(cx.tcx(), ty) { + if cx.shared().type_is_sized(ty) { Type::i8p(cx) } else { Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, ty)], false) @@ -104,7 +102,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ // FIXME(eddyb) Temporary sanity check for ty::layout. let layout = cx.layout_of(t); - if !type_is_sized(cx.tcx(), t) { + if !cx.shared().type_is_sized(t) { if !layout.is_unsized() { bug!("layout should be unsized for type `{}` / {:#?}", t, layout); @@ -135,7 +133,7 @@ pub fn fat_ptr_base_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> match ty.sty { ty::TyBox(t) | ty::TyRef(_, ty::TypeAndMut { ty: t, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty: t, .. }) if !type_is_sized(ccx.tcx(), t) => { + ty::TyRawPtr(ty::TypeAndMut { ty: t, .. }) if !ccx.shared().type_is_sized(t) => { in_memory_type_of(ccx, t).ptr_to() } _ => bug!("expected fat ptr ty but got {:?}", ty) @@ -172,7 +170,7 @@ pub fn immediate_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> /// is too large for it to be placed in SSA value (by our rules). /// For the raw type without far pointer indirection, see `in_memory_type_of`. pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type { - let ty = if !type_is_sized(cx.tcx(), ty) { + let ty = if !cx.shared().type_is_sized(ty) { cx.tcx().mk_imm_ptr(ty) } else { ty @@ -232,7 +230,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ty::TyBox(ty) | ty::TyRef(_, ty::TypeAndMut{ty, ..}) | ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => { - if !type_is_sized(cx.tcx(), ty) { + if !cx.shared().type_is_sized(ty) { if let ty::TyStr = ty.sty { // This means we get a nicer name in the output (str is always // unsized). diff --git a/src/librustc_trans/value.rs b/src/librustc_trans/value.rs index 79e0c11515f..287ad87caac 100644 --- a/src/librustc_trans/value.rs +++ b/src/librustc_trans/value.rs @@ -9,16 +9,11 @@ // except according to those terms. use llvm; -use llvm::{UseRef, ValueRef}; -use basic_block::BasicBlock; -use common::Block; use std::fmt; -use libc::c_uint; - #[derive(Copy, Clone, PartialEq)] -pub struct Value(pub ValueRef); +pub struct Value(pub llvm::ValueRef); impl fmt::Debug for Value { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -27,152 +22,3 @@ impl fmt::Debug for Value { }).expect("nun-UTF8 value description from LLVM")) } } - -macro_rules! opt_val { ($e:expr) => ( - unsafe { - match $e { - p if !p.is_null() => Some(Value(p)), - _ => None - } - } -) } - -/// Wrapper for LLVM ValueRef -impl Value { - /// Returns the native ValueRef - pub fn get(&self) -> ValueRef { - let Value(v) = *self; v - } - - /// Returns the BasicBlock that contains this value - pub fn get_parent(self) -> Option<BasicBlock> { - unsafe { - match llvm::LLVMGetInstructionParent(self.get()) { - p if !p.is_null() => Some(BasicBlock(p)), - _ => None - } - } - } - - /// Removes this value from its containing BasicBlock - pub fn erase_from_parent(self) { - unsafe { - llvm::LLVMInstructionEraseFromParent(self.get()); - } - } - - /// Returns the single dominating store to this value, if any - /// This only performs a search for a trivially dominating store. The store - /// must be the only user of this value, and there must not be any conditional - /// branches between the store and the given block. - pub fn get_dominating_store(self, bcx: Block) -> Option<Value> { - match self.get_single_user().and_then(|user| user.as_store_inst()) { - Some(store) => { - store.get_parent().and_then(|store_bb| { - let mut bb = BasicBlock(bcx.llbb); - let mut ret = Some(store); - while bb.get() != store_bb.get() { - match bb.get_single_predecessor() { - Some(pred) => bb = pred, - None => { ret = None; break } - } - } - ret - }) - } - _ => None - } - } - - /// Returns the first use of this value, if any - pub fn get_first_use(self) -> Option<Use> { - unsafe { - match llvm::LLVMGetFirstUse(self.get()) { - u if !u.is_null() => Some(Use(u)), - _ => None - } - } - } - - /// Tests if there are no uses of this value - pub fn has_no_uses(self) -> bool { - self.get_first_use().is_none() - } - - /// Returns the single user of this value - /// If there are no users or multiple users, this returns None - pub fn get_single_user(self) -> Option<Value> { - let mut iter = self.user_iter(); - match (iter.next(), iter.next()) { - (Some(first), None) => Some(first), - _ => None - } - } - - /// Returns an iterator for the users of this value - pub fn user_iter(self) -> Users { - Users { - next: self.get_first_use() - } - } - - /// Returns the requested operand of this instruction - /// Returns None, if there's no operand at the given index - pub fn get_operand(self, i: usize) -> Option<Value> { - opt_val!(llvm::LLVMGetOperand(self.get(), i as c_uint)) - } - - /// Returns the Store represent by this value, if any - pub fn as_store_inst(self) -> Option<Value> { - opt_val!(llvm::LLVMIsAStoreInst(self.get())) - } - - /// Tests if this value is a terminator instruction - pub fn is_a_terminator_inst(self) -> bool { - unsafe { - !llvm::LLVMIsATerminatorInst(self.get()).is_null() - } - } -} - -/// Wrapper for LLVM UseRef -#[derive(Copy, Clone)] -pub struct Use(UseRef); - -impl Use { - pub fn get(&self) -> UseRef { - let Use(v) = *self; v - } - - pub fn get_user(self) -> Value { - unsafe { - Value(llvm::LLVMGetUser(self.get())) - } - } - - pub fn get_next_use(self) -> Option<Use> { - unsafe { - match llvm::LLVMGetNextUse(self.get()) { - u if !u.is_null() => Some(Use(u)), - _ => None - } - } - } -} - -/// Iterator for the users of a value -pub struct Users { - next: Option<Use> -} - -impl Iterator for Users { - type Item = Value; - - fn next(&mut self) -> Option<Value> { - let current = self.next; - - self.next = current.and_then(|u| u.get_next_use()); - - current.map(|u| u.get_user()) - } -} diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 71270963f80..598003a3925 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1519,14 +1519,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { self.set_tainted_by_errors(); return self.tcx().types.err; } - _ => { - struct_span_err!(tcx.sess, span, E0248, - "found value `{}` used as a type", - tcx.item_path_str(path.def.def_id())) - .span_label(span, &format!("value used as a type")) - .emit(); - return self.tcx().types.err; - } + _ => span_bug!(span, "unexpected definition: {:?}", path.def) } } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 624201eaab6..ff50ee11b39 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -481,7 +481,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } else { (result_ty, arm_ty) }; - self.report_mismatched_types(&cause, expected, found, e); + self.report_mismatched_types(&cause, expected, found, e).emit(); self.tcx.types.err } }; diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index e72dba858c5..b4647df3f4f 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -131,10 +131,18 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { Some(self.fcx.resolve_type_vars_if_possible(&normalized.value)) } + /// Returns the final type, generating an error if it is an + /// unresolved inference variable. pub fn unambiguous_final_ty(&self) -> Ty<'tcx> { self.fcx.structurally_resolved_type(self.span, self.cur_ty) } + /// Returns the final type we ended up with, which may well be an + /// inference variable (we will resolve it first, if possible). + pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> { + self.fcx.resolve_type_vars_if_possible(&self.cur_ty) + } + pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I) where I: IntoIterator<Item = &'b hir::Expr> { diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index f2c8ef46a7e..265dcada1f8 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -142,20 +142,21 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { fn report_cast_error(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, e: CastError) { match e { CastError::NeedDeref => { + let error_span = self.span; let cast_ty = fcx.ty_to_string(self.cast_ty); - let mut err = fcx.type_error_struct(self.cast_span, + let mut err = fcx.type_error_struct(error_span, |actual| { format!("casting `{}` as `{}` is invalid", actual, cast_ty) }, self.expr_ty); - err.span_label(self.expr.span, + err.span_label(error_span, &format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty)); if let Ok(snippet) = fcx.sess().codemap().span_to_snippet(self.expr.span) { - err.span_label(self.expr.span, + err.span_help(self.expr.span, &format!("did you mean `*{}`?", snippet)); } err.emit(); diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index ef1c08bdab5..393d9341a08 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -14,8 +14,13 @@ use rustc::ty::Ty; use rustc::infer::{InferOk}; use rustc::traits::ObligationCause; -use syntax_pos::Span; +use syntax::ast; +use syntax_pos::{self, Span}; use rustc::hir; +use rustc::hir::def::Def; +use rustc::ty::{self, AssociatedItem}; + +use super::method::probe; impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Requires that the two types unify, and prints an error message if @@ -27,7 +32,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.register_predicates(obligations); }, Err(e) => { - self.report_mismatched_types(&cause, expected, actual, e); + self.report_mismatched_types(&cause, expected, actual, e).emit(); } } } @@ -46,7 +51,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.register_predicates(obligations); }, Err(e) => { - self.report_mismatched_types(cause, expected, actual, e); + self.report_mismatched_types(cause, expected, actual, e).emit(); } } } @@ -57,7 +62,65 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Err(e) = self.try_coerce(expr, checked_ty, expected) { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); - self.report_mismatched_types(&cause, expected, expr_ty, e); + let mode = probe::Mode::MethodCall; + let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, + mode, + expected, + checked_ty, + ast::DUMMY_NODE_ID); + let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); + if suggestions.len() > 0 { + err.help(&format!("here are some functions which \ + might fulfill your needs:\n - {}", + self.get_best_match(&suggestions))); + }; + err.emit(); + } + } + + fn format_method_suggestion(&self, method: &AssociatedItem) -> String { + format!(".{}({})", + method.name, + if self.has_no_input_arg(method) { + "" + } else { + "..." + }) + } + + fn display_suggested_methods(&self, methods: &[AssociatedItem]) -> String { + methods.iter() + .take(5) + .map(|method| self.format_method_suggestion(&*method)) + .collect::<Vec<String>>() + .join("\n - ") + } + + fn get_best_match(&self, methods: &[AssociatedItem]) -> String { + let no_argument_methods: Vec<_> = + methods.iter() + .filter(|ref x| self.has_no_input_arg(&*x)) + .map(|x| x.clone()) + .collect(); + if no_argument_methods.len() > 0 { + self.display_suggested_methods(&no_argument_methods) + } else { + self.display_suggested_methods(&methods) + } + } + + // This function checks if the method isn't static and takes other arguments than `self`. + fn has_no_input_arg(&self, method: &AssociatedItem) -> bool { + match method.def() { + Def::Method(def_id) => { + match self.tcx.item_type(def_id).sty { + ty::TypeVariants::TyFnDef(_, _, fty) => { + fty.sig.skip_binder().inputs().len() == 1 + } + _ => false, + } + } + _ => false, } } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index b29eab780e0..9b8e77301e5 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -30,9 +30,11 @@ pub use self::CandidateSource::*; pub use self::suggest::AllTraitsVec; mod confirm; -mod probe; +pub mod probe; mod suggest; +use self::probe::IsSuggestion; + pub enum MethodError<'tcx> { // Did not find an applicable method, but we did find various near-misses that may work. NoMatch(NoMatchData<'tcx>), @@ -91,7 +93,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { allow_private: bool) -> bool { let mode = probe::Mode::MethodCall; - match self.probe_method(span, mode, method_name, self_ty, call_expr_id) { + match self.probe_for_name(span, mode, method_name, IsSuggestion(false), + self_ty, call_expr_id) { Ok(..) => true, Err(NoMatch(..)) => false, Err(Ambiguity(..)) => true, @@ -130,7 +133,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mode = probe::Mode::MethodCall; let self_ty = self.resolve_type_vars_if_possible(&self_ty); - let pick = self.probe_method(span, mode, method_name, self_ty, call_expr.id)?; + let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false), + self_ty, call_expr.id)?; if let Some(import_id) = pick.import_id { self.tcx.used_trait_imports.borrow_mut().insert(import_id); @@ -328,7 +332,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr_id: ast::NodeId) -> Result<Def, MethodError<'tcx>> { let mode = probe::Mode::Path; - let pick = self.probe_method(span, mode, method_name, self_ty, expr_id)?; + let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false), + self_ty, expr_id)?; if let Some(import_id) = pick.import_id { self.tcx.used_trait_imports.borrow_mut().insert(import_id); @@ -339,7 +344,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.tcx.check_stability(def.def_id(), expr_id, span); if let probe::InherentImplPick = pick.kind { - if !pick.item.vis.is_accessible_from(self.body_id, &self.tcx.map) { + if !self.tcx.vis_is_accessible_from(pick.item.vis, self.body_id) { let msg = format!("{} `{}` is private", def.kind_name(), method_name); self.tcx.sess.span_err(span, &msg); } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 5cb0804b1bc..1962534c397 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -16,12 +16,12 @@ use super::suggest; use check::FnCtxt; use hir::def_id::DefId; use hir::def::Def; -use rustc::infer::InferOk; use rustc::ty::subst::{Subst, Substs}; use rustc::traits::{self, ObligationCause}; use rustc::ty::{self, Ty, ToPolyTraitRef, TraitRef, TypeFoldable}; use rustc::infer::type_variable::TypeVariableOrigin; use rustc::util::nodemap::FxHashSet; +use rustc::infer::{self, InferOk}; use syntax::ast; use syntax_pos::Span; use rustc::hir; @@ -32,11 +32,24 @@ use std::rc::Rc; use self::CandidateKind::*; pub use self::PickKind::*; +pub enum LookingFor<'tcx> { + /// looking for methods with the given name; this is the normal case + MethodName(ast::Name), + + /// looking for methods that return a given type; this is used to + /// assemble suggestions + ReturnType(Ty<'tcx>), +} + +/// Boolean flag used to indicate if this search is for a suggestion +/// or not. If true, we can allow ambiguity and so forth. +pub struct IsSuggestion(pub bool); + struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, span: Span, mode: Mode, - item_name: ast::Name, + looking_for: LookingFor<'tcx>, steps: Rc<Vec<CandidateStep<'tcx>>>, opt_simplified_steps: Option<Vec<ty::fast_reject::SimplifiedType>>, inherent_candidates: Vec<Candidate<'tcx>>, @@ -144,18 +157,72 @@ pub enum Mode { } impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { - pub fn probe_method(&self, - span: Span, - mode: Mode, - item_name: ast::Name, - self_ty: Ty<'tcx>, - scope_expr_id: ast::NodeId) - -> PickResult<'tcx> { + /// This is used to offer suggestions to users. It returns methods + /// that could have been called which have the desired return + /// type. Some effort is made to rule out methods that, if called, + /// would result in an error (basically, the same criteria we + /// would use to decide if a method is a plausible fit for + /// ambiguity purposes). + pub fn probe_for_return_type(&self, + span: Span, + mode: Mode, + return_type: Ty<'tcx>, + self_ty: Ty<'tcx>, + scope_expr_id: ast::NodeId) + -> Vec<ty::AssociatedItem> { + debug!("probe(self_ty={:?}, return_type={}, scope_expr_id={})", + self_ty, + return_type, + scope_expr_id); + let method_names = + self.probe_op(span, mode, LookingFor::ReturnType(return_type), IsSuggestion(true), + self_ty, scope_expr_id, + |probe_cx| Ok(probe_cx.candidate_method_names())) + .unwrap_or(vec![]); + method_names + .iter() + .flat_map(|&method_name| { + match self.probe_for_name(span, mode, method_name, IsSuggestion(true), self_ty, + scope_expr_id) { + Ok(pick) => Some(pick.item), + Err(_) => None, + } + }) + .collect() + } + + pub fn probe_for_name(&self, + span: Span, + mode: Mode, + item_name: ast::Name, + is_suggestion: IsSuggestion, + self_ty: Ty<'tcx>, + scope_expr_id: ast::NodeId) + -> PickResult<'tcx> { debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})", self_ty, item_name, scope_expr_id); + self.probe_op(span, + mode, + LookingFor::MethodName(item_name), + is_suggestion, + self_ty, + scope_expr_id, + |probe_cx| probe_cx.pick()) + } + fn probe_op<OP,R>(&'a self, + span: Span, + mode: Mode, + looking_for: LookingFor<'tcx>, + is_suggestion: IsSuggestion, + self_ty: Ty<'tcx>, + scope_expr_id: ast::NodeId, + op: OP) + -> Result<R, MethodError<'tcx>> + where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result<R, MethodError<'tcx>> + { // FIXME(#18741) -- right now, creating the steps involves evaluating the // `*` operator, which registers obligations that then escape into // the global fulfillment context and thus has global @@ -164,7 +231,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // think cause spurious errors. Really though this part should // take place in the `self.probe` below. let steps = if mode == Mode::MethodCall { - match self.create_steps(span, self_ty) { + match self.create_steps(span, self_ty, is_suggestion) { Some(steps) => steps, None => { return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(), @@ -207,14 +274,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // that we create during the probe process are removed later self.probe(|_| { let mut probe_cx = - ProbeContext::new(self, span, mode, item_name, steps, opt_simplified_steps); + ProbeContext::new(self, span, mode, looking_for, + steps, opt_simplified_steps); probe_cx.assemble_inherent_candidates(); probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?; - probe_cx.pick() + op(probe_cx) }) } - fn create_steps(&self, span: Span, self_ty: Ty<'tcx>) -> Option<Vec<CandidateStep<'tcx>>> { + fn create_steps(&self, + span: Span, + self_ty: Ty<'tcx>, + is_suggestion: IsSuggestion) + -> Option<Vec<CandidateStep<'tcx>>> { // FIXME: we don't need to create the entire steps in one pass let mut autoderef = self.autoderef(span, self_ty); @@ -228,8 +300,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }) .collect(); - let final_ty = autoderef.unambiguous_final_ty(); + let final_ty = autoderef.maybe_ambiguous_final_ty(); match final_ty.sty { + ty::TyInfer(ty::TyVar(_)) => { + // Ended in an inference variable. If we are doing + // a real method lookup, this is a hard error (it's an + // ambiguity and we can't make progress). + if !is_suggestion.0 { + let t = self.structurally_resolved_type(span, final_ty); + assert_eq!(t, self.tcx.types.err); + return None + } else { + // If we're just looking for suggestions, + // though, ambiguity is no big thing, we can + // just ignore it. + } + } ty::TyArray(elem_ty, _) => { let dereferences = steps.len() - 1; @@ -253,7 +339,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, span: Span, mode: Mode, - item_name: ast::Name, + looking_for: LookingFor<'tcx>, steps: Vec<CandidateStep<'tcx>>, opt_simplified_steps: Option<Vec<ty::fast_reject::SimplifiedType>>) -> ProbeContext<'a, 'gcx, 'tcx> { @@ -261,7 +347,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fcx: fcx, span: span, mode: mode, - item_name: item_name, + looking_for: looking_for, inherent_candidates: Vec::new(), extension_candidates: Vec::new(), impl_dups: FxHashSet(), @@ -410,44 +496,40 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { debug!("assemble_inherent_impl_probe {:?}", impl_def_id); - let item = match self.associated_item(impl_def_id) { - Some(m) => m, - None => { - return; - } // No method with correct name on this impl - }; + for item in self.impl_or_trait_item(impl_def_id) { + if !self.has_applicable_self(&item) { + // No receiver declared. Not a candidate. + self.record_static_candidate(ImplSource(impl_def_id)); + continue + } - if !self.has_applicable_self(&item) { - // No receiver declared. Not a candidate. - return self.record_static_candidate(ImplSource(impl_def_id)); - } + if !self.tcx.vis_is_accessible_from(item.vis, self.body_id) { + self.private_candidate = Some(item.def()); + continue + } - if !item.vis.is_accessible_from(self.body_id, &self.tcx.map) { - self.private_candidate = Some(item.def()); - return; - } + let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id); + let impl_ty = impl_ty.subst(self.tcx, impl_substs); - let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id); - let impl_ty = impl_ty.subst(self.tcx, impl_substs); - - // Determine the receiver type that the method itself expects. - let xform_self_ty = self.xform_self_ty(&item, impl_ty, impl_substs); - - // We can't use normalize_associated_types_in as it will pollute the - // fcx's fulfillment context after this probe is over. - let cause = traits::ObligationCause::misc(self.span, self.body_id); - let mut selcx = &mut traits::SelectionContext::new(self.fcx); - let traits::Normalized { value: xform_self_ty, obligations } = - traits::normalize(selcx, cause, &xform_self_ty); - debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}", - xform_self_ty); - - self.inherent_candidates.push(Candidate { - xform_self_ty: xform_self_ty, - item: item, - kind: InherentImplCandidate(impl_substs, obligations), - import_id: self.import_id, - }); + // Determine the receiver type that the method itself expects. + let xform_self_ty = self.xform_self_ty(&item, impl_ty, impl_substs); + + // We can't use normalize_associated_types_in as it will pollute the + // fcx's fulfillment context after this probe is over. + let cause = traits::ObligationCause::misc(self.span, self.body_id); + let mut selcx = &mut traits::SelectionContext::new(self.fcx); + let traits::Normalized { value: xform_self_ty, obligations } = + traits::normalize(selcx, cause, &xform_self_ty); + debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}", + xform_self_ty); + + self.inherent_candidates.push(Candidate { + xform_self_ty: xform_self_ty, + item: item, + kind: InherentImplCandidate(impl_substs, obligations), + import_id: self.import_id, + }); + } } fn assemble_inherent_candidates_from_object(&mut self, @@ -540,17 +622,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let tcx = self.tcx; for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { - let item = match self.associated_item(bound_trait_ref.def_id()) { - Some(v) => v, - None => { - continue; + for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { + if !self.has_applicable_self(&item) { + self.record_static_candidate(TraitSource(bound_trait_ref.def_id())); + } else { + mk_cand(self, bound_trait_ref, item); } - }; - - if !self.has_applicable_self(&item) { - self.record_static_candidate(TraitSource(bound_trait_ref.def_id())); - } else { - mk_cand(self, bound_trait_ref, item); } } } @@ -584,36 +661,45 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { Ok(()) } + pub fn matches_return_type(&self, method: &ty::AssociatedItem, + expected: ty::Ty<'tcx>) -> bool { + match method.def() { + Def::Method(def_id) => { + let fty = self.tcx.item_type(def_id).fn_sig(); + self.probe(|_| { + let substs = self.fresh_substs_for_item(self.span, method.def_id); + let output = fty.output().subst(self.tcx, substs); + let (output, _) = self.replace_late_bound_regions_with_fresh_var( + self.span, infer::FnCall, &output); + self.can_sub_types(output, expected).is_ok() + }) + } + _ => false, + } + } + fn assemble_extension_candidates_for_trait(&mut self, trait_def_id: DefId) -> Result<(), MethodError<'tcx>> { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); - // Check whether `trait_def_id` defines a method with suitable name: - let maybe_item = self.tcx.associated_items(trait_def_id) - .find(|item| item.name == self.item_name); - let item = match maybe_item { - Some(i) => i, - None => { - return Ok(()); + for item in self.impl_or_trait_item(trait_def_id) { + // Check whether `trait_def_id` defines a method with suitable name: + if !self.has_applicable_self(&item) { + debug!("method has inapplicable self"); + self.record_static_candidate(TraitSource(trait_def_id)); + continue; } - }; - // Check whether `trait_def_id` defines a method with suitable name: - if !self.has_applicable_self(&item) { - debug!("method has inapplicable self"); - self.record_static_candidate(TraitSource(trait_def_id)); - return Ok(()); - } + self.assemble_extension_candidates_for_trait_impls(trait_def_id, item.clone()); - self.assemble_extension_candidates_for_trait_impls(trait_def_id, item.clone()); + self.assemble_closure_candidates(trait_def_id, item.clone())?; - self.assemble_closure_candidates(trait_def_id, item.clone())?; + self.assemble_projection_candidates(trait_def_id, item.clone()); - self.assemble_projection_candidates(trait_def_id, item.clone()); - - self.assemble_where_clause_candidates(trait_def_id, item.clone()); + self.assemble_where_clause_candidates(trait_def_id, item.clone()); + } Ok(()) } @@ -833,10 +919,30 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { } } + fn candidate_method_names(&self) -> Vec<ast::Name> { + let mut set = FxHashSet(); + let mut names: Vec<_> = + self.inherent_candidates + .iter() + .chain(&self.extension_candidates) + .map(|candidate| candidate.item.name) + .filter(|&name| set.insert(name)) + .collect(); + + // sort them by the name so we have a stable result + names.sort_by_key(|n| n.as_str()); + names + } + /////////////////////////////////////////////////////////////////////////// // THE ACTUAL SEARCH fn pick(mut self) -> PickResult<'tcx> { + assert!(match self.looking_for { + LookingFor::MethodName(_) => true, + LookingFor::ReturnType(_) => false, + }); + if let Some(r) = self.pick_core() { return r; } @@ -855,6 +961,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let out_of_scope_traits = match self.pick_core() { Some(Ok(p)) => vec![p.item.container.id()], + //Some(Ok(p)) => p.iter().map(|p| p.item.container().id()).collect(), Some(Err(MethodError::Ambiguity(v))) => { v.into_iter() .map(|source| { @@ -1257,10 +1364,20 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self.tcx.erase_late_bound_regions(value) } - /// Find item with name `item_name` defined in impl/trait `def_id` - /// and return it, or `None`, if no such item was defined there. - fn associated_item(&self, def_id: DefId) -> Option<ty::AssociatedItem> { - self.fcx.associated_item(def_id, self.item_name) + /// Find the method with the appropriate name (or return type, as the case may be). + fn impl_or_trait_item(&self, def_id: DefId) -> Vec<ty::AssociatedItem> { + match self.looking_for { + LookingFor::MethodName(name) => { + self.fcx.associated_item(def_id, name).map_or(Vec::new(), |x| vec![x]) + } + LookingFor::ReturnType(return_ty) => { + self.tcx + .associated_items(def_id) + .map(|did| self.tcx.associated_item(did.def_id)) + .filter(|m| self.matches_return_type(m, return_ty)) + .collect() + } + } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 58dff935a16..a2dceed8d26 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2986,7 +2986,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } Err(e) => { - self.report_mismatched_types(&cause, expected_ty, found_ty, e); + self.report_mismatched_types(&cause, expected_ty, found_ty, e).emit(); self.tcx.types.err } } @@ -3009,7 +3009,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { debug!("struct named {:?}", base_t); if let Some(field) = base_def.struct_variant().find_field_named(field.node) { let field_ty = self.field_ty(expr.span, field, substs); - if field.vis.is_accessible_from(self.body_id, &self.tcx().map) { + if self.tcx.vis_is_accessible_from(field.vis, self.body_id) { autoderef.finalize(lvalue_pref, Some(base)); self.write_autoderef_adjustment(base.id, autoderefs, base_t); @@ -3116,7 +3116,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { base_def.struct_variant().fields.get(idx.node).and_then(|field| { let field_ty = self.field_ty(expr.span, field, substs); private_candidate = Some((base_def.did, field_ty)); - if field.vis.is_accessible_from(self.body_id, &self.tcx().map) { + if self.tcx.vis_is_accessible_from(field.vis, self.body_id) { self.tcx.check_stability(field.did, expr.id, expr.span); Some(field_ty) } else { @@ -3697,7 +3697,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { match result { Ok(ty) => ctxt.unified = ty, Err(err) => { - self.report_mismatched_types(&cause, ctxt.unified, e_ty, err); + self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(); } } @@ -3880,7 +3880,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { match result { Ok(ty) => unified = ty, Err(e) => { - self.report_mismatched_types(&cause, unified, e_ty, e); + self.report_mismatched_types(&cause, unified, e_ty, e).emit(); } } } diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index f575d4d8bab..0e5a16987c1 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -356,7 +356,7 @@ impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> { infcx.report_mismatched_types(&cause, mk_ptr(mt_b.ty), target, - ty::error::TypeError::Mutability); + ty::error::TypeError::Mutability).emit(); } (mt_a.ty, mt_b.ty, unsize_trait, None) }; diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 71507063ffc..cea3ad43a95 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1378,8 +1378,8 @@ let x = |_| {}; // error: cannot determine a type for this expression You have two possibilities to solve this situation: - * Give an explicit definition of the expression - * Infer the expression +* Give an explicit definition of the expression +* Infer the expression Examples: @@ -2861,25 +2861,6 @@ struct Bar<S, T> { x: Foo<S, T> } ``` "##, -E0248: r##" -This error indicates an attempt to use a value where a type is expected. For -example: - -```compile_fail,E0248 -enum Foo { - Bar(u32) -} - -fn do_something(x: Foo::Bar) { } -``` - -In this example, we're attempting to take a type of `Foo::Bar` in the -do_something function. This is not legal: `Foo::Bar` is a value of type `Foo`, -not a distinct static type. Likewise, it's not legal to attempt to -`impl Foo::Bar`: instead, you must `impl Foo` and then pattern match to specify -behavior for specific enum variants. -"##, - E0569: r##" If an impl has a generic parameter with the `#[may_dangle]` attribute, then that impl must be declared as an `unsafe impl. For example: @@ -4247,6 +4228,7 @@ register_diagnostics! { E0245, // not a trait // E0246, // invalid recursive type // E0247, +// E0248, // value used as a type, now reported earlier during resolution as E0412 // E0249, // E0319, // trait impls for defaulted traits allowed just for structs/enums E0320, // recursive overflow during dropck diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 50d4c3cd0c9..ec17813ed2a 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -185,7 +185,7 @@ fn require_same_types<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, true } Err(err) => { - infcx.report_mismatched_types(cause, expected, actual, err); + infcx.report_mismatched_types(cause, expected, actual, err).emit(); false } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 28ca92f5db6..fdbd2f3647c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1737,7 +1737,6 @@ impl Clean<Type> for hir::Ty { segments.pop(); let trait_path = hir::Path { span: p.span, - global: p.global, def: Def::Trait(cx.tcx.associated_item(p.def.def_id()).container.id()), segments: segments.into(), }; @@ -1756,7 +1755,6 @@ impl Clean<Type> for hir::Ty { } let trait_path = hir::Path { span: self.span, - global: false, def: def, segments: vec![].into(), }; @@ -2213,9 +2211,9 @@ impl Path { impl Clean<Path> for hir::Path { fn clean(&self, cx: &DocContext) -> Path { Path { - global: self.global, + global: self.is_global(), def: self.def, - segments: self.segments.clean(cx), + segments: if self.is_global() { &self.segments[1..] } else { &self.segments }.clean(cx), } } } @@ -2270,24 +2268,19 @@ impl Clean<PathSegment> for hir::PathSegment { } fn qpath_to_string(p: &hir::QPath) -> String { - let (segments, global) = match *p { - hir::QPath::Resolved(_, ref path) => { - (&path.segments, path.global) - } - hir::QPath::TypeRelative(_, ref segment) => { - return segment.name.to_string() - } + let segments = match *p { + hir::QPath::Resolved(_, ref path) => &path.segments, + hir::QPath::TypeRelative(_, ref segment) => return segment.name.to_string(), }; let mut s = String::new(); - let mut first = true; - for i in segments.iter().map(|x| x.name.as_str()) { - if !first || global { + for (i, seg) in segments.iter().enumerate() { + if i > 0 { s.push_str("::"); - } else { - first = false; } - s.push_str(&i); + if seg.name != keywords::CrateRoot.name() { + s.push_str(&*seg.name.as_str()); + } } s } @@ -2801,7 +2794,7 @@ impl Clean<Item> for doctree::Macro { visibility: Some(Public), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), - def_id: cx.tcx.map.local_def_id(self.id), + def_id: self.def_id, inner: MacroItem(Macro { source: format!("macro_rules! {} {{\n{}}}", name, diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 21fc135eaad..31e10fbd3b7 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -233,9 +233,11 @@ pub struct DefaultImpl { pub whence: Span, } +// For Macro we store the DefId instead of the NodeId, since we also create +// these imported macro_rules (which only have a DUMMY_NODE_ID). pub struct Macro { pub name: Name, - pub id: ast::NodeId, + pub def_id: hir::def_id::DefId, pub attrs: hir::HirVec<ast::Attribute>, pub whence: Span, pub matchers: hir::HirVec<Span>, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 6dc6e80dae0..796a3a93068 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -207,7 +207,7 @@ impl<'a> fmt::Display for WhereClause<'a> { if !f.alternate() { clause.push_str("</span>"); let plain = format!("{:#}", self); - if plain.len() > 80 { + if plain.len() + pad > 80 { //break it onto its own line regardless, but make sure method impls and trait //blocks keep their fixed padding (2 and 9, respectively) let padding = if pad > 10 { diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 5353642e294..06a81641296 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -46,6 +46,7 @@ r##"<!DOCTYPE html> <title>{title}</title> + <link rel="stylesheet" type="text/css" href="{root_path}normalize.css"> <link rel="stylesheet" type="text/css" href="{root_path}rustdoc.css"> <link rel="stylesheet" type="text/css" href="{root_path}main.css"> {css_extension} diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index e21898499a3..1d7fb2cd238 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -591,7 +591,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { ty: item.type_(), name: item.name.clone().unwrap(), path: fqp[..fqp.len() - 1].join("::"), - desc: Escape(&shorter(item.doc_value())).to_string(), + desc: plain_summary_line(item.doc_value()), parent: Some(did), parent_idx: None, search_type: get_index_search_type(&item), @@ -629,7 +629,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { } let crate_doc = krate.module.as_ref().map(|module| { - Escape(&shorter(module.doc_value())).to_string() + plain_summary_line(module.doc_value()) }).unwrap_or(String::new()); let mut crate_data = BTreeMap::new(); @@ -1064,7 +1064,7 @@ impl DocFolder for Cache { ty: item.type_(), name: s.to_string(), path: path.join("::").to_string(), - desc: Escape(&shorter(item.doc_value())).to_string(), + desc: plain_summary_line(item.doc_value()), parent: parent, parent_idx: None, search_type: get_index_search_type(&item), @@ -1468,6 +1468,13 @@ impl<'a> Item<'a> { return None; } } else { + // Macros from other libraries get special filenames which we can + // safely ignore. + if self.item.source.filename.starts_with("<") && + self.item.source.filename.ends_with("macros>") { + return None; + } + let (krate, src_root) = match cache.extern_locations.get(&self.item.def_id.krate) { Some(&(ref name, ref src, Local)) => (name, src), Some(&(ref name, ref src, Remote(ref s))) => { @@ -1655,8 +1662,13 @@ fn document_full(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result { } fn document_stability(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result { - for stability in short_stability(item, cx, true) { - write!(w, "<div class='stability'>{}</div>", stability)?; + let stabilities = short_stability(item, cx, true); + if !stabilities.is_empty() { + write!(w, "<div class='stability'>")?; + for stability in stabilities { + write!(w, "{}", stability)?; + } + write!(w, "</div>")?; } Ok(()) } @@ -1855,7 +1867,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec<S String::new() }; let text = format!("Deprecated{}{}", since, MarkdownHtml(&deprecated_reason)); - stability.push(format!("<em class='stab deprecated'>{}</em>", text)) + stability.push(format!("<div class='stab deprecated'>{}</div>", text)) }; if stab.level == stability::Unstable { @@ -1880,7 +1892,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec<S String::new() }; let text = format!("Unstable{}{}", unstable_extra, MarkdownHtml(&unstable_reason)); - stability.push(format!("<em class='stab unstable'>{}</em>", text)) + stability.push(format!("<div class='stab unstable'>{}</div>", text)) }; } else if let Some(depr) = item.deprecation.as_ref() { let note = if show_reason && !depr.note.is_empty() { @@ -1895,7 +1907,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec<S }; let text = format!("Deprecated{}{}", since, MarkdownHtml(¬e)); - stability.push(format!("<em class='stab deprecated'>{}</em>", text)) + stability.push(format!("<div class='stab deprecated'>{}</div>", text)) } stability diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 6ea25fa1241..c12e1e7d608 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -609,7 +609,7 @@ displayPath + '<span class="' + type + '">' + name + '</span></a></td><td>' + '<a href="' + href + '">' + - '<span class="desc">' + item.desc + + '<span class="desc">' + escape(item.desc) + ' </span></a></td></tr>'; }); } else { @@ -807,14 +807,6 @@ search(); } - function plainSummaryLine(markdown) { - markdown.replace(/\n/g, ' ') - .replace(/'/g, "\'") - .replace(/^#+? (.+?)/, "$1") - .replace(/\[(.*?)\]\(.*?\)/g, "$1") - .replace(/\[(.*?)\]\[.*?\]/g, "$1"); - } - index = buildIndex(rawSearchIndex); startSearch(); @@ -836,13 +828,10 @@ if (crates[i] === window.currentCrate) { klass += ' current'; } - if (rawSearchIndex[crates[i]].items[0]) { - var desc = rawSearchIndex[crates[i]].items[0][3]; - var link = $('<a>', {'href': '../' + crates[i] + '/index.html', - 'title': plainSummaryLine(desc), - 'class': klass}).text(crates[i]); - ul.append($('<li>').append(link)); - } + var link = $('<a>', {'href': '../' + crates[i] + '/index.html', + 'title': rawSearchIndex[crates[i]].doc, + 'class': klass}).text(crates[i]); + ul.append($('<li>').append(link)); } sidebar.append(div); } diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 7ee184c089c..0ca4f7ea36f 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -1,5 +1,3 @@ -@import "normalize.css"; - /** * Copyright 2013 The Rust Project Developers. See the COPYRIGHT * file at the top-level directory of this distribution and at @@ -52,13 +50,15 @@ font-family: 'Source Code Pro'; font-style: normal; font-weight: 400; - src: local('Source Code Pro'), url("SourceCodePro-Regular.woff") format('woff'); + /* Avoid using locally installed font because bad versions are in circulation: + * see https://github.com/rust-lang/rust/issues/24355 */ + src: url("SourceCodePro-Regular.woff") format('woff'); } @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 600; - src: local('Source Code Pro Semibold'), url("SourceCodePro-Semibold.woff") format('woff'); + src: url("SourceCodePro-Semibold.woff") format('woff'); } * { @@ -523,20 +523,20 @@ body.blur > :not(#help) { padding: 20px; } -em.stab { - display: inline-block; +.stab { + display: table; border-width: 1px; border-style: solid; padding: 3px; margin-bottom: 5px; font-size: 90%; - font-style: normal; } -em.stab p { +.stab p { display: inline; } .module-item .stab { + display: inline; border-width: 0; padding: 0; margin: 0; diff --git a/src/librustdoc/html/static/styles/main.css b/src/librustdoc/html/static/styles/main.css index 6a9a24f6996..862d6d12b9a 100644 --- a/src/librustdoc/html/static/styles/main.css +++ b/src/librustdoc/html/static/styles/main.css @@ -30,10 +30,6 @@ h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.t background-color: white; } -div.stability > em > code { - background-color: initial; -} - .docblock code, .docblock-short code { background-color: #F5F5F5; } @@ -129,5 +125,5 @@ a.test-arrow { background-color: white; } -em.stab.unstable { background: #FFF5D6; border-color: #FFC600; } -em.stab.deprecated { background: #F3DFFF; border-color: #7F0087; } +.stab.unstable { background: #FFF5D6; border-color: #FFC600; } +.stab.deprecated { background: #F3DFFF; border-color: #7F0087; } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 74c7bc10194..835825d31ee 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -54,6 +54,9 @@ extern crate serialize as rustc_serialize; // used by deriving use std::collections::{BTreeMap, BTreeSet}; use std::default::Default; use std::env; +use std::fmt::Display; +use std::io; +use std::io::Write; use std::path::PathBuf; use std::process; use std::sync::mpsc::channel; @@ -183,7 +186,7 @@ pub fn main_args(args: &[String]) -> isize { let matches = match getopts::getopts(&args[1..], &all_groups) { Ok(m) => m, Err(err) => { - println!("{}", err); + print_error(err); return 1; } }; @@ -211,11 +214,11 @@ pub fn main_args(args: &[String]) -> isize { } if matches.free.is_empty() { - println!("expected an input file to act on"); + print_error("missing file operand"); return 1; } if matches.free.len() > 1 { - println!("only one input file may be specified"); + print_error("too many file operands"); return 1; } let input = &matches.free[0]; @@ -227,7 +230,7 @@ pub fn main_args(args: &[String]) -> isize { let externs = match parse_externs(&matches) { Ok(ex) => ex, Err(err) => { - println!("{}", err); + print_error(err); return 1; } }; @@ -247,7 +250,10 @@ pub fn main_args(args: &[String]) -> isize { if let Some(ref p) = css_file_extension { if !p.is_file() { - println!("{}", "--extend-css option must take a css file as input"); + writeln!( + &mut io::stderr(), + "rustdoc: option --extend-css argument must be a file." + ).unwrap(); return 1; } } @@ -257,7 +263,7 @@ pub fn main_args(args: &[String]) -> isize { &matches.opt_strs("html-before-content"), &matches.opt_strs("html-after-content")) { Some(eh) => eh, - None => return 3 + None => return 3, }; let crate_name = matches.opt_str("crate-name"); let playground_url = matches.opt_str("playground-url"); @@ -291,17 +297,26 @@ pub fn main_args(args: &[String]) -> isize { 0 } Some(s) => { - println!("unknown output format: {}", s); + print_error(format!("unknown output format: {}", s)); 1 } } }); res.unwrap_or_else(|s| { - println!("input error: {}", s); + print_error(format!("input error: {}", s)); 1 }) } +/// Prints an uniformised error message on the standard error output +fn print_error<T>(error_message: T) where T: Display { + writeln!( + &mut io::stderr(), + "rustdoc: {}\nTry 'rustdoc --help' for more information.", + error_message + ).unwrap(); +} + /// Looks inside the command line arguments to extract the relevant input format /// and files and then generates the necessary rustdoc output for formatting. fn acquire_input<R, F>(input: &str, diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 29267960a4a..9dbc9d30e60 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -71,7 +71,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, let mut out = match File::create(&output) { Err(e) => { let _ = writeln!(&mut io::stderr(), - "error opening `{}` for writing: {}", + "rustdoc: {}: {}", output.display(), e); return 4; } @@ -80,8 +80,10 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, let (metadata, text) = extract_leading_metadata(&input_str); if metadata.is_empty() { - let _ = writeln!(&mut io::stderr(), - "invalid markdown file: expecting initial line with `% ...TITLE...`"); + let _ = writeln!( + &mut io::stderr(), + "rustdoc: invalid markdown file: expecting initial line with `% ...TITLE...`" + ); return 5; } let title = metadata[0]; @@ -132,7 +134,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, match err { Err(e) => { let _ = writeln!(&mut io::stderr(), - "error writing to `{}`: {}", + "rustdoc: cannot write to `{}`: {}", output.display(), e); 6 } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 4087b9a761f..c93112657b9 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -85,7 +85,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { None); // attach the crate's exported macros to the top-level module: let macro_exports: Vec<_> = - krate.exported_macros.iter().map(|def| self.visit_macro(def)).collect(); + krate.exported_macros.iter().map(|def| self.visit_local_macro(def)).collect(); self.module.macros.extend(macro_exports); self.module.is_crate = true; } @@ -201,6 +201,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { if def_id.krate == LOCAL_CRATE { continue // These are `krate.exported_macros`, handled in `self.visit()`. } + let imported_from = self.cx.sess().cstore.original_crate_name(def_id.krate); let def = match self.cx.sess().cstore.load_macro(def_id, self.cx.sess()) { LoadedMacro::MacroRules(macro_rules) => macro_rules, // FIXME(jseyfried): document proc macro reexports @@ -210,14 +211,14 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // FIXME(jseyfried) merge with `self.visit_macro()` let matchers = def.body.chunks(4).map(|arm| arm[0].get_span()).collect(); om.macros.push(Macro { - id: def.id, + def_id: def_id, attrs: def.attrs.clone().into(), name: def.ident.name, whence: def.span, matchers: matchers, stab: self.stability(def.id), depr: self.deprecation(def.id), - imported_from: def.imported_from.map(|ident| ident.name), + imported_from: Some(imported_from), }) } } @@ -513,19 +514,19 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } // convert each exported_macro into a doc item - fn visit_macro(&self, def: &hir::MacroDef) -> Macro { + fn visit_local_macro(&self, def: &hir::MacroDef) -> Macro { // Extract the spans of all matchers. They represent the "interface" of the macro. let matchers = def.body.chunks(4).map(|arm| arm[0].get_span()).collect(); Macro { - id: def.id, + def_id: self.cx.tcx.map.local_def_id(def.id), attrs: def.attrs.clone(), name: def.name, whence: def.span, matchers: matchers, stab: self.stability(def.id), depr: self.deprecation(def.id), - imported_from: def.imported_from, + imported_from: None, } } } diff --git a/src/libserialize/leb128.rs b/src/libserialize/leb128.rs index 8e8e03f1f8e..5b72c6d46ac 100644 --- a/src/libserialize/leb128.rs +++ b/src/libserialize/leb128.rs @@ -9,18 +9,26 @@ // except according to those terms. #[inline] -pub fn write_to_vec(vec: &mut Vec<u8>, position: &mut usize, byte: u8) { - if *position == vec.len() { +fn write_to_vec(vec: &mut Vec<u8>, position: usize, byte: u8) { + if position == vec.len() { vec.push(byte); } else { - vec[*position] = byte; + vec[position] = byte; } - - *position += 1; } -pub fn write_unsigned_leb128(out: &mut Vec<u8>, start_position: usize, mut value: u64) -> usize { - let mut position = start_position; +#[inline] +/// encodes an integer using unsigned leb128 encoding and stores +/// the result using a callback function. +/// +/// The callback `write` is called once for each position +/// that is to be written to with the byte to be encoded +/// at that position. +pub fn write_unsigned_leb128_to<W>(mut value: u64, mut write: W) -> usize + where W: FnMut(usize, u8) +{ + let mut position = 0; + loop { let mut byte = (value & 0x7F) as u8; value >>= 7; @@ -28,14 +36,19 @@ pub fn write_unsigned_leb128(out: &mut Vec<u8>, start_position: usize, mut value byte |= 0x80; } - write_to_vec(out, &mut position, byte); + write(position, byte); + position += 1; if value == 0 { break; } } - return position - start_position; + position +} + +pub fn write_unsigned_leb128(out: &mut Vec<u8>, start_position: usize, value: u64) -> usize { + write_unsigned_leb128_to(value, |i, v| write_to_vec(out, start_position+i, v)) } #[inline] @@ -56,9 +69,17 @@ pub fn read_unsigned_leb128(data: &[u8], start_position: usize) -> (u64, usize) (result, position - start_position) } - -pub fn write_signed_leb128(out: &mut Vec<u8>, start_position: usize, mut value: i64) -> usize { - let mut position = start_position; +#[inline] +/// encodes an integer using signed leb128 encoding and stores +/// the result using a callback function. +/// +/// The callback `write` is called once for each position +/// that is to be written to with the byte to be encoded +/// at that position. +pub fn write_signed_leb128_to<W>(mut value: i64, mut write: W) -> usize + where W: FnMut(usize, u8) +{ + let mut position = 0; loop { let mut byte = (value as u8) & 0x7f; @@ -69,14 +90,19 @@ pub fn write_signed_leb128(out: &mut Vec<u8>, start_position: usize, mut value: byte |= 0x80; // Mark this byte to show that more bytes will follow. } - write_to_vec(out, &mut position, byte); + write(position, byte); + position += 1; if !more { break; } } - return position - start_position; + position +} + +pub fn write_signed_leb128(out: &mut Vec<u8>, start_position: usize, value: i64) -> usize { + write_signed_leb128_to(value, |i, v| write_to_vec(out, start_position+i, v)) } #[inline] diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index a063b856468..f5e9ec6d89d 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -12,6 +12,7 @@ #![stable(feature = "rust1", since = "1.0.0")] +use fmt; use mem; use ops::Range; use iter::FusedIterator; @@ -370,6 +371,13 @@ impl ExactSizeIterator for EscapeDefault {} #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for EscapeDefault {} +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for EscapeDefault { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("EscapeDefault { .. }") + } +} + static ASCII_LOWERCASE_MAP: [u8; 256] = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 1087d1f2447..5e1c3a28515 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -26,7 +26,7 @@ fn main() { let target = env::var("TARGET").expect("TARGET was not set"); let host = env::var("HOST").expect("HOST was not set"); if cfg!(feature = "backtrace") && !target.contains("apple") && !target.contains("msvc") && - !target.contains("emscripten") && !target.contains("fuchsia") { + !target.contains("emscripten") && !target.contains("fuchsia") && !target.contains("redox") { build_libbacktrace(&host, &target); } @@ -104,7 +104,7 @@ fn build_libbacktrace(host: &str, target: &str) { .env("AR", &ar) .env("RANLIB", format!("{} s", ar.display())) .env("CFLAGS", cflags)); - run(Command::new("make") + run(Command::new(build_helper::make(host)) .current_dir(&build_dir) .arg(format!("INCDIR={}", src_dir.display())) .arg("-j").arg(env::var("NUM_JOBS").expect("NUM_JOBS was not set"))); diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 0b310eb2585..2fa3a9c4844 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1276,6 +1276,15 @@ impl<'a, K, V> Clone for Iter<'a, K, V> { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, K: Debug, V: Debug> fmt::Debug for Iter<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.clone()) + .finish() + } +} + /// HashMap mutable values iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct IterMut<'a, K: 'a, V: 'a> { @@ -1285,7 +1294,7 @@ pub struct IterMut<'a, K: 'a, V: 'a> { /// HashMap move iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter<K, V> { - inner: table::IntoIter<K, V>, + pub(super) inner: table::IntoIter<K, V>, } /// HashMap keys iterator. @@ -1302,6 +1311,15 @@ impl<'a, K, V> Clone for Keys<'a, K, V> { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, K: Debug, V: Debug> fmt::Debug for Keys<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.clone()) + .finish() + } +} + /// HashMap values iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct Values<'a, K: 'a, V: 'a> { @@ -1316,10 +1334,19 @@ impl<'a, K, V> Clone for Values<'a, K, V> { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, K: Debug, V: Debug> fmt::Debug for Values<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.clone()) + .finish() + } +} + /// HashMap drain iterator. #[stable(feature = "drain", since = "1.6.0")] pub struct Drain<'a, K: 'a, V: 'a> { - inner: table::Drain<'a, K, V>, + pub(super) inner: table::Drain<'a, K, V>, } /// Mutable HashMap values iterator. @@ -1557,6 +1584,18 @@ impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { #[unstable(feature = "fused", issue = "35602")] impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, K, V> fmt::Debug for IterMut<'a, K, V> + where K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.inner.iter()) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<K, V> Iterator for IntoIter<K, V> { type Item = (K, V); @@ -1580,6 +1619,15 @@ impl<K, V> ExactSizeIterator for IntoIter<K, V> { #[unstable(feature = "fused", issue = "35602")] impl<K, V> FusedIterator for IntoIter<K, V> {} +#[stable(feature = "std_debug", since = "1.15.0")] +impl<K: Debug, V: Debug> fmt::Debug for IntoIter<K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.inner.iter()) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Iterator for Keys<'a, K, V> { type Item = &'a K; @@ -1649,6 +1697,18 @@ impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { #[unstable(feature = "fused", issue = "35602")] impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, K, V> fmt::Debug for ValuesMut<'a, K, V> + where K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.inner.inner.iter()) + .finish() + } +} + #[stable(feature = "drain", since = "1.6.0")] impl<'a, K, V> Iterator for Drain<'a, K, V> { type Item = (K, V); @@ -1672,6 +1732,18 @@ impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> { #[unstable(feature = "fused", issue = "35602")] impl<'a, K, V> FusedIterator for Drain<'a, K, V> {} +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, K, V> fmt::Debug for Drain<'a, K, V> + where K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.inner.iter()) + .finish() + } +} + impl<'a, K, V> Entry<'a, K, V> { #[stable(feature = "rust1", since = "1.0.0")] /// Ensures a value is in the entry by inserting the default if empty, and returns @@ -2148,6 +2220,13 @@ impl Default for RandomState { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for RandomState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("RandomState { .. }") + } +} + impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> where K: Eq + Hash + Borrow<Q>, S: BuildHasher, diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 72af612f569..341b050862f 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -948,6 +948,15 @@ impl<'a, K> ExactSizeIterator for Iter<'a, K> { #[unstable(feature = "fused", issue = "35602")] impl<'a, K> FusedIterator for Iter<'a, K> {} +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, K: fmt::Debug> fmt::Debug for Iter<'a, K> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.clone()) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<K> Iterator for IntoIter<K> { type Item = K; @@ -968,6 +977,16 @@ impl<K> ExactSizeIterator for IntoIter<K> { #[unstable(feature = "fused", issue = "35602")] impl<K> FusedIterator for IntoIter<K> {} +#[stable(feature = "std_debug", since = "1.15.0")] +impl<K: fmt::Debug> fmt::Debug for IntoIter<K> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let entries_iter = self.iter.inner.iter().map(|(k, _)| k); + f.debug_list() + .entries(entries_iter) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K> Iterator for Drain<'a, K> { type Item = K; @@ -988,6 +1007,16 @@ impl<'a, K> ExactSizeIterator for Drain<'a, K> { #[unstable(feature = "fused", issue = "35602")] impl<'a, K> FusedIterator for Drain<'a, K> {} +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, K: fmt::Debug> fmt::Debug for Drain<'a, K> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let entries_iter = self.iter.inner.iter().map(|(k, _)| k); + f.debug_list() + .entries(entries_iter) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Clone for Intersection<'a, T, S> { fn clone(&self) -> Intersection<'a, T, S> { @@ -1021,6 +1050,18 @@ impl<'a, T, S> Iterator for Intersection<'a, T, S> } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, T, S> fmt::Debug for Intersection<'a, T, S> + where T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.clone()) + .finish() + } +} + #[unstable(feature = "fused", issue = "35602")] impl<'a, T, S> FusedIterator for Intersection<'a, T, S> where T: Eq + Hash, @@ -1068,6 +1109,18 @@ impl<'a, T, S> FusedIterator for Difference<'a, T, S> { } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, T, S> fmt::Debug for Difference<'a, T, S> + where T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.clone()) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Clone for SymmetricDifference<'a, T, S> { fn clone(&self) -> SymmetricDifference<'a, T, S> { @@ -1097,6 +1150,18 @@ impl<'a, T, S> FusedIterator for SymmetricDifference<'a, T, S> { } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, T, S> fmt::Debug for SymmetricDifference<'a, T, S> + where T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.clone()) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Clone for Union<'a, T, S> { fn clone(&self) -> Union<'a, T, S> { @@ -1111,6 +1176,18 @@ impl<'a, T, S> FusedIterator for Union<'a, T, S> { } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, T, S> fmt::Debug for Union<'a, T, S> + where T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries(self.clone()) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Iterator for Union<'a, T, S> where T: Eq + Hash, diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index a784d8e50f9..2cd9362a657 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -882,6 +882,15 @@ unsafe impl<'a, K: Sync, V: Sync> Sync for IterMut<'a, K, V> {} // but Send is the more useful bound unsafe impl<'a, K: Send, V: Send> Send for IterMut<'a, K, V> {} +impl<'a, K: 'a, V: 'a> IterMut<'a, K, V> { + pub fn iter(&self) -> Iter<K, V> { + Iter { + iter: self.iter.clone(), + elems_left: self.elems_left, + } + } +} + /// Iterator over the entries in a table, consuming the table. pub struct IntoIter<K, V> { table: RawTable<K, V>, @@ -891,6 +900,15 @@ pub struct IntoIter<K, V> { unsafe impl<K: Sync, V: Sync> Sync for IntoIter<K, V> {} unsafe impl<K: Send, V: Send> Send for IntoIter<K, V> {} +impl<K, V> IntoIter<K, V> { + pub fn iter(&self) -> Iter<K, V> { + Iter { + iter: self.iter.clone(), + elems_left: self.table.size, + } + } +} + /// Iterator over the entries in a table, clearing the table. pub struct Drain<'a, K: 'a, V: 'a> { table: Shared<RawTable<K, V>>, @@ -901,6 +919,17 @@ pub struct Drain<'a, K: 'a, V: 'a> { unsafe impl<'a, K: Sync, V: Sync> Sync for Drain<'a, K, V> {} unsafe impl<'a, K: Send, V: Send> Send for Drain<'a, K, V> {} +impl<'a, K, V> Drain<'a, K, V> { + pub fn iter(&self) -> Iter<K, V> { + unsafe { + Iter { + iter: self.iter.clone(), + elems_left: (**self.table).size, + } + } + } +} + impl<'a, K, V> Iterator for Iter<'a, K, V> { type Item = (&'a K, &'a V); diff --git a/src/libstd/env.rs b/src/libstd/env.rs index ee6a907f616..0521f301321 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -143,6 +143,13 @@ impl Iterator for Vars { fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Vars { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Vars { .. }") + } +} + #[stable(feature = "env", since = "1.0.0")] impl Iterator for VarsOs { type Item = (OsString, OsString); @@ -150,6 +157,13 @@ impl Iterator for VarsOs { fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for VarsOs { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("VarsOs { .. }") + } +} + /// Fetches the environment variable `key` from the current process. /// /// The returned result is `Ok(s)` if the environment variable is present and is @@ -364,6 +378,13 @@ impl<'a> Iterator for SplitPaths<'a> { fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a> fmt::Debug for SplitPaths<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("SplitPaths { .. }") + } +} + /// Error type returned from `std::env::join_paths` when paths fail to be /// joined. #[derive(Debug)] @@ -640,6 +661,13 @@ impl DoubleEndedIterator for Args { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Args { .. }") + } +} + #[stable(feature = "env", since = "1.0.0")] impl Iterator for ArgsOs { type Item = OsString; @@ -657,6 +685,14 @@ impl ExactSizeIterator for ArgsOs { impl DoubleEndedIterator for ArgsOs { fn next_back(&mut self) -> Option<OsString> { self.inner.next_back() } } + +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for ArgsOs { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("ArgsOs { .. }") + } +} + /// Constants associated with the current target #[stable(feature = "env", since = "1.0.0")] pub mod consts { diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index e91e808c548..41934dc057e 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -35,21 +35,53 @@ use time::SystemTime; /// /// # Examples /// +/// Create a new file and write bytes to it: +/// /// ```no_run +/// use std::fs::File; /// use std::io::prelude::*; +/// +/// # fn foo() -> std::io::Result<()> { +/// let mut file = try!(File::create("foo.txt")); +/// try!(file.write_all(b"Hello, world!")); +/// # Ok(()) +/// # } +/// ``` +/// +/// Read the contents of a file into a `String`: +/// +/// ```no_run /// use std::fs::File; +/// use std::io::prelude::*; /// /// # fn foo() -> std::io::Result<()> { -/// let mut f = try!(File::create("foo.txt")); -/// try!(f.write_all(b"Hello, world!")); +/// let mut file = try!(File::open("foo.txt")); +/// let mut contents = String::new(); +/// try!(file.read_to_string(&mut contents)); +/// assert_eq!(contents, "Hello, world!"); +/// # Ok(()) +/// # } +/// ``` /// -/// let mut f = try!(File::open("foo.txt")); -/// let mut s = String::new(); -/// try!(f.read_to_string(&mut s)); -/// assert_eq!(s, "Hello, world!"); +/// It can be more efficient to read the contents of a file with a buffered +/// [`Read`]er. This can be accomplished with [`BufReader<R>`]: +/// +/// ```no_run +/// use std::fs::File; +/// use std::io::BufReader; +/// use std::io::prelude::*; +/// +/// # fn foo() -> std::io::Result<()> { +/// let file = try!(File::open("foo.txt")); +/// let mut buf_reader = BufReader::new(file); +/// let mut contents = String::new(); +/// try!(buf_reader.read_to_string(&mut contents)); +/// assert_eq!(contents, "Hello, world!"); /// # Ok(()) /// # } /// ``` +/// +/// [`BufReader`]: ../io/struct.BufReader.html #[stable(feature = "rust1", since = "1.0.0")] pub struct File { inner: fs_imp::File, @@ -140,7 +172,7 @@ pub struct DirEntry(fs_imp::DirEntry); /// .create(true) /// .open("foo.txt"); /// ``` -#[derive(Clone)] +#[derive(Clone, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct OpenOptions(fs_imp::OpenOptions); @@ -168,6 +200,7 @@ pub struct FileType(fs_imp::FileType); /// /// This builder also supports platform-specific options. #[stable(feature = "dir_builder", since = "1.6.0")] +#[derive(Debug)] pub struct DirBuilder { inner: fs_imp::DirBuilder, recursive: bool, @@ -834,6 +867,21 @@ impl Metadata { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Metadata { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Metadata") + .field("file_type", &self.file_type()) + .field("is_dir", &self.is_dir()) + .field("is_file", &self.is_file()) + .field("permissions", &self.permissions()) + .field("modified", &self.modified()) + .field("accessed", &self.accessed()) + .field("created", &self.created()) + .finish() + } +} + impl AsInner<fs_imp::FileAttr> for Metadata { fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 } } @@ -1751,6 +1799,16 @@ mod tests { } ) } + #[cfg(windows)] + macro_rules! error { ($e:expr, $s:expr) => ( + match $e { + Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), + Err(ref err) => assert!(err.raw_os_error() == Some($s), + format!("`{}` did not have a code of `{}`", err, $s)) + } + ) } + + #[cfg(unix)] macro_rules! error { ($e:expr, $s:expr) => ( match $e { Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), @@ -1771,12 +1829,9 @@ mod tests { match symlink_file(r"nonexisting_target", link) { Ok(_) => true, - Err(ref err) => - if err.to_string().contains("A required privilege is not held by the client.") { - false - } else { - true - } + // ERROR_PRIVILEGE_NOT_HELD = 1314 + Err(ref err) if err.raw_os_error() == Some(1314) => false, + Err(_) => true, } } @@ -1807,12 +1862,10 @@ mod tests { let filename = &tmpdir.join("file_that_does_not_exist.txt"); let result = File::open(filename); - if cfg!(unix) { - error!(result, "No such file or directory"); - } - if cfg!(windows) { - error!(result, "The system cannot find the file specified"); - } + #[cfg(unix)] + error!(result, "No such file or directory"); + #[cfg(windows)] + error!(result, 2); // ERROR_FILE_NOT_FOUND } #[test] @@ -1822,12 +1875,10 @@ mod tests { let result = fs::remove_file(filename); - if cfg!(unix) { - error!(result, "No such file or directory"); - } - if cfg!(windows) { - error!(result, "The system cannot find the file specified"); - } + #[cfg(unix)] + error!(result, "No such file or directory"); + #[cfg(windows)] + error!(result, 2); // ERROR_FILE_NOT_FOUND } #[test] @@ -2582,8 +2633,10 @@ mod tests { let mut a = OO::new(); a.append(true); let mut ra = OO::new(); ra.read(true).append(true); - let invalid_options = if cfg!(windows) { "The parameter is incorrect" } - else { "Invalid argument" }; + #[cfg(windows)] + let invalid_options = 87; // ERROR_INVALID_PARAMETER + #[cfg(unix)] + let invalid_options = "Invalid argument"; // Test various combinations of creation modes and access modes. // diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index cd7a50d07e2..c15a1c8328c 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -652,6 +652,7 @@ impl<W> fmt::Display for IntoInnerError<W> { #[stable(feature = "rust1", since = "1.0.0")] pub struct LineWriter<W: Write> { inner: BufWriter<W>, + need_flush: bool, } impl<W: Write> LineWriter<W> { @@ -692,7 +693,10 @@ impl<W: Write> LineWriter<W> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(cap: usize, inner: W) -> LineWriter<W> { - LineWriter { inner: BufWriter::with_capacity(cap, inner) } + LineWriter { + inner: BufWriter::with_capacity(cap, inner), + need_flush: false, + } } /// Gets a reference to the underlying writer. @@ -759,7 +763,10 @@ impl<W: Write> LineWriter<W> { #[stable(feature = "rust1", since = "1.0.0")] pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> { self.inner.into_inner().map_err(|IntoInnerError(buf, e)| { - IntoInnerError(LineWriter { inner: buf }, e) + IntoInnerError(LineWriter { + inner: buf, + need_flush: false, + }, e) }) } } @@ -767,20 +774,46 @@ impl<W: Write> LineWriter<W> { #[stable(feature = "rust1", since = "1.0.0")] impl<W: Write> Write for LineWriter<W> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - match memchr::memrchr(b'\n', buf) { - Some(i) => { - let n = self.inner.write(&buf[..i + 1])?; - if n != i + 1 || self.inner.flush().is_err() { - // Do not return errors on partial writes. - return Ok(n); - } - self.inner.write(&buf[i + 1..]).map(|i| n + i) - } - None => self.inner.write(buf), + if self.need_flush { + self.flush()?; + } + + // Find the last newline character in the buffer provided. If found then + // we're going to write all the data up to that point and then flush, + // otherewise we just write the whole block to the underlying writer. + let i = match memchr::memrchr(b'\n', buf) { + Some(i) => i, + None => return self.inner.write(buf), + }; + + + // Ok, we're going to write a partial amount of the data given first + // followed by flushing the newline. After we've successfully written + // some data then we *must* report that we wrote that data, so future + // errors are ignored. We set our internal `need_flush` flag, though, in + // case flushing fails and we need to try it first next time. + let n = self.inner.write(&buf[..i + 1])?; + self.need_flush = true; + if self.flush().is_err() || n != i + 1 { + return Ok(n) + } + + // At this point we successfully wrote `i + 1` bytes and flushed it out, + // meaning that the entire line is now flushed out on the screen. While + // we can attempt to finish writing the rest of the data provided. + // Remember though that we ignore errors here as we've successfully + // written data, so we need to report that. + match self.inner.write(&buf[i + 1..]) { + Ok(i) => Ok(n + i), + Err(_) => Ok(n), } } - fn flush(&mut self) -> io::Result<()> { self.inner.flush() } + fn flush(&mut self) -> io::Result<()> { + self.inner.flush()?; + self.need_flush = false; + Ok(()) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1153,4 +1186,44 @@ mod tests { BufWriter::new(io::sink()) }); } + + struct AcceptOneThenFail { + written: bool, + flushed: bool, + } + + impl Write for AcceptOneThenFail { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + if !self.written { + assert_eq!(data, b"a\nb\n"); + self.written = true; + Ok(data.len()) + } else { + Err(io::Error::new(io::ErrorKind::NotFound, "test")) + } + } + + fn flush(&mut self) -> io::Result<()> { + assert!(self.written); + assert!(!self.flushed); + self.flushed = true; + Err(io::Error::new(io::ErrorKind::Other, "test")) + } + } + + #[test] + fn erroneous_flush_retried() { + let a = AcceptOneThenFail { + written: false, + flushed: false, + }; + + let mut l = LineWriter::new(a); + assert_eq!(l.write(b"a\nb\na").unwrap(), 4); + assert!(l.get_ref().written); + assert!(l.get_ref().flushed); + l.get_mut().flushed = false; + + assert_eq!(l.write(b"a").unwrap_err().kind(), io::ErrorKind::Other) + } } diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index b3b89213df1..5450a10c9bd 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1425,6 +1425,12 @@ pub trait BufRead: Read { /// println!("{}", line.unwrap()); /// } /// ``` + /// + /// # Errors + /// + /// Each line of the iterator has the same error semantics as [`BufRead::read_line()`]. + /// + /// [`BufRead::read_line()`]: trait.BufRead.html#method.read_line #[stable(feature = "rust1", since = "1.0.0")] fn lines(self) -> Lines<Self> where Self: Sized { Lines { buf: self } @@ -1444,6 +1450,16 @@ pub struct Chain<T, U> { done_first: bool, } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<T: fmt::Debug, U: fmt::Debug> fmt::Debug for Chain<T, U> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Chain") + .field("t", &self.first) + .field("u", &self.second) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<T: Read, U: Read> Read for Chain<T, U> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> { @@ -1485,6 +1501,7 @@ impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> { /// /// [`take()`]: trait.Read.html#method.take #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct Take<T> { inner: T, limit: u64, @@ -1526,8 +1543,6 @@ impl<T> Take<T> { /// # Examples /// /// ``` - /// #![feature(io_take_into_inner)] - /// /// use std::io; /// use std::io::prelude::*; /// use std::fs::File; @@ -1543,7 +1558,7 @@ impl<T> Take<T> { /// # Ok(()) /// # } /// ``` - #[unstable(feature = "io_take_into_inner", issue = "23755")] + #[stable(feature = "io_take_into_inner", since = "1.15.0")] pub fn into_inner(self) -> T { self.inner } @@ -1604,6 +1619,7 @@ fn read_one_byte(reader: &mut Read) -> Option<Result<u8>> { /// /// [`bytes()`]: trait.Read.html#method.bytes #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct Bytes<R> { inner: R, } @@ -1625,6 +1641,7 @@ impl<R: Read> Iterator for Bytes<R> { /// [chars]: trait.Read.html#method.chars #[unstable(feature = "io", reason = "awaiting stability of Read::chars", issue = "27802")] +#[derive(Debug)] pub struct Chars<R> { inner: R, } @@ -1714,6 +1731,7 @@ impl fmt::Display for CharsError { /// /// [split]: trait.BufRead.html#method.split #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct Split<B> { buf: B, delim: u8, @@ -1745,6 +1763,7 @@ impl<B: BufRead> Iterator for Split<B> { /// /// [lines]: trait.BufRead.html#method.lines #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct Lines<B> { buf: B, } diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index e8b812daed8..9d1c8942f8c 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -10,7 +10,7 @@ use io::prelude::*; -use cell::{RefCell, BorrowState}; +use cell::RefCell; use fmt; use io::lazy::Lazy; use io::{self, BufReader, LineWriter}; @@ -81,11 +81,11 @@ impl Read for StdinRaw { } impl Write for StdoutRaw { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } - fn flush(&mut self) -> io::Result<()> { Ok(()) } + fn flush(&mut self) -> io::Result<()> { self.0.flush() } } impl Write for StderrRaw { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } - fn flush(&mut self) -> io::Result<()> { Ok(()) } + fn flush(&mut self) -> io::Result<()> { self.0.flush() } } enum Maybe<T> { @@ -282,6 +282,13 @@ impl Stdin { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Stdin { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Stdin { .. }") + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { @@ -314,6 +321,13 @@ impl<'a> BufRead for StdinLock<'a> { fn consume(&mut self, n: usize) { self.inner.consume(n) } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a> fmt::Debug for StdinLock<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("StdinLock { .. }") + } +} + /// A handle to the global standard output stream of the current process. /// /// Each handle shares a global buffer of data to be written to the standard @@ -424,6 +438,13 @@ impl Stdout { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Stdout { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Stdout { .. }") + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { @@ -449,6 +470,13 @@ impl<'a> Write for StdoutLock<'a> { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a> fmt::Debug for StdoutLock<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("StdoutLock { .. }") + } +} + /// A handle to the standard error stream of a process. /// /// For more information, see the [`io::stderr`] method. @@ -545,6 +573,13 @@ impl Stderr { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Stderr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Stderr { .. }") + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { @@ -570,6 +605,13 @@ impl<'a> Write for StderrLock<'a> { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a> fmt::Debug for StderrLock<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("StderrLock { .. }") + } +} + /// Resets the thread-local stderr handle to the specified writer /// /// This will replace the current thread's stderr handle, returning the old @@ -638,8 +680,8 @@ pub fn _print(args: fmt::Arguments) { LocalKeyState::Destroyed => stdout().write_fmt(args), LocalKeyState::Valid => { LOCAL_STDOUT.with(|s| { - if s.borrow_state() == BorrowState::Unused { - if let Some(w) = s.borrow_mut().as_mut() { + if let Ok(mut borrowed) = s.try_borrow_mut() { + if let Some(w) = borrowed.as_mut() { return w.write_fmt(args); } } diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index 2c6880281b5..436511031ef 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -10,6 +10,7 @@ #![allow(missing_copy_implementations)] +use fmt; use io::{self, Read, Write, ErrorKind, BufRead}; /// Copies the entire contents of a reader into a writer. @@ -97,6 +98,13 @@ impl BufRead for Empty { fn consume(&mut self, _n: usize) {} } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Empty { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Empty { .. }") + } +} + /// A reader which yields one byte over and over and over and over and over and... /// /// This struct is generally created by calling [`repeat()`][repeat]. Please @@ -133,6 +141,13 @@ impl Read for Repeat { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Repeat { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Repeat { .. }") + } +} + /// A writer which will move data into the void. /// /// This struct is generally created by calling [`sink()`][sink]. Please @@ -165,6 +180,13 @@ impl Write for Sink { fn flush(&mut self) -> io::Result<()> { Ok(()) } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Sink { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Sink { .. }") + } +} + #[cfg(test)] mod tests { use io::prelude::*; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 414f25fa5eb..fc5c6968544 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -214,6 +214,7 @@ #![no_std] #![deny(missing_docs)] +#![deny(missing_debug_implementations)] // Tell the compiler to link to either panic_abort or panic_unwind #![needs_panic_runtime] @@ -276,6 +277,7 @@ #![feature(panic_unwind)] #![feature(placement_in_syntax)] #![feature(prelude_import)] +#![feature(pub_restricted)] #![feature(rand)] #![feature(raw)] #![feature(repr_simd)] diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index c1e610f33fb..6aab7486004 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -1068,6 +1068,14 @@ impl From<[u8; 16]> for Ipv6Addr { } } +#[stable(feature = "ipv6_from_segments", since = "1.15.0")] +impl From<[u16; 8]> for Ipv6Addr { + fn from(segments: [u16; 8]) -> Ipv6Addr { + let [a, b, c, d, e, f, g, h] = segments; + Ipv6Addr::new(a, b, c, d, e, f, g, h) + } +} + // Tests for this module #[cfg(all(test, not(target_os = "emscripten")))] mod tests { @@ -1413,11 +1421,29 @@ mod tests { } #[test] - fn ipv4_from_u32_slice() { + fn ipv4_from_octets() { assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1)) } #[test] + fn ipv6_from_segments() { + let from_u16s = Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, + 0x8899, 0xaabb, 0xccdd, 0xeeff]); + let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, + 0x8899, 0xaabb, 0xccdd, 0xeeff); + assert_eq!(new, from_u16s); + } + + #[test] + fn ipv6_from_octets() { + let from_u16s = Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, + 0x8899, 0xaabb, 0xccdd, 0xeeff]); + let from_u8s = Ipv6Addr::from([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]); + assert_eq!(from_u16s, from_u8s); + } + + #[test] fn ord() { assert!(Ipv4Addr::new(100, 64, 3, 3) < Ipv4Addr::new(192, 0, 2, 2)); assert!("2001:db8:f00::1002".parse::<Ipv6Addr>().unwrap() < diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index 56286fbe253..cadf87f32b1 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -12,6 +12,7 @@ #![stable(feature = "rust1", since = "1.0.0")] +use fmt; use io::{self, Error, ErrorKind}; use sys_common::net as net_imp; @@ -105,6 +106,13 @@ impl Iterator for LookupHost { fn next(&mut self) -> Option<SocketAddr> { self.0.next() } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for LookupHost { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("LookupHost { .. }") + } +} + /// Resolve the host specified by `host` as a number of `SocketAddr` instances. /// /// This method may perform a DNS query to resolve `host` and may also inspect diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index be9636a0a19..63817c9f10f 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -76,6 +76,7 @@ pub struct TcpListener(net_imp::TcpListener); /// [`incoming`]: struct.TcpListener.html#method.incoming /// [`TcpListener`]: struct.TcpListener.html #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct Incoming<'a> { listener: &'a TcpListener } impl TcpStream { diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index b280f466dd4..f8a5ec0b379 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -499,6 +499,19 @@ impl UdpSocket { /// This will retrieve the stored error in the underlying socket, clearing /// the field in the process. This can be useful for checking errors between /// calls. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// match socket.take_error() { + /// Ok(Some(error)) => println!("UdpSocket error: {:?}", error), + /// Ok(None) => println!("No error"), + /// Err(error) => println!("UdpSocket.take_error failed: {:?}", error), + /// } + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() @@ -507,6 +520,15 @@ impl UdpSocket { /// Connects this UDP socket to a remote address, allowing the `send` and /// `recv` syscalls to be used to send data and also applies filters to only /// receive data from the specified address. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.connect("127.0.0.1:8080").expect("connect function failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> { super::each_addr(addr, |addr| self.0.connect(addr)) @@ -514,8 +536,20 @@ impl UdpSocket { /// Sends data on the socket to the remote address to which it is connected. /// - /// The `connect` method will connect this socket to a remote address. This + /// The [`connect()`] method will connect this socket to a remote address. This /// method will fail if the socket is not connected. + /// + /// [`connect()`]: #method.connect + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.connect("127.0.0.1:8080").expect("connect function failed"); + /// socket.send(&[0, 1, 2]).expect("couldn't send message"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn send(&self, buf: &[u8]) -> io::Result<usize> { self.0.send(buf) @@ -526,6 +560,20 @@ impl UdpSocket { /// /// The `connect` method will connect this socket to a remote address. This /// method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.connect("127.0.0.1:8080").expect("connect function failed"); + /// let mut buf = [0; 10]; + /// match socket.recv(&mut buf) { + /// Ok(received) => println!("received {} bytes", received), + /// Err(e) => println!("recv function failed: {:?}", e), + /// } + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { self.0.recv(buf) @@ -535,6 +583,15 @@ impl UdpSocket { /// /// On Unix this corresponds to calling fcntl, and on Windows this /// corresponds to calling ioctlsocket. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_nonblocking(true).expect("set_nonblocking call failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) diff --git a/src/libstd/os/linux/raw.rs b/src/libstd/os/linux/raw.rs index e6a95bc831f..7c9274d0601 100644 --- a/src/libstd/os/linux/raw.rs +++ b/src/libstd/os/linux/raw.rs @@ -17,6 +17,7 @@ crates.io should be used instead for the correct \ definitions")] #![allow(deprecated)] +#![allow(missing_debug_implementations)] use os::raw::c_ulong; diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs index 366a1674156..e45af867055 100644 --- a/src/libstd/os/mod.rs +++ b/src/libstd/os/mod.rs @@ -11,9 +11,9 @@ //! OS-specific functionality. #![stable(feature = "os", since = "1.0.0")] -#![allow(missing_docs, bad_style)] +#![allow(missing_docs, bad_style, missing_debug_implementations)] -#[cfg(unix)] +#[cfg(any(target_os = "redox", unix))] #[stable(feature = "rust1", since = "1.0.0")] pub use sys::ext as unix; #[cfg(windows)] diff --git a/src/libstd/os/raw.rs b/src/libstd/os/raw.rs index 2a918d8aeb7..cc154f7ab41 100644 --- a/src/libstd/os/raw.rs +++ b/src/libstd/os/raw.rs @@ -12,6 +12,8 @@ #![stable(feature = "raw_os", since = "1.1.0")] +use fmt; + #[cfg(any(target_os = "android", target_os = "emscripten", all(target_os = "linux", any(target_arch = "aarch64", @@ -71,6 +73,13 @@ pub enum c_void { #[doc(hidden)] __variant2, } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for c_void { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("c_void") + } +} + #[cfg(test)] #[allow(unused_imports)] mod tests { diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index a7e8c4fab37..faf4949e861 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -14,6 +14,7 @@ use any::Any; use cell::UnsafeCell; +use fmt; use ops::{Deref, DerefMut}; use panicking; use ptr::{Unique, Shared}; @@ -296,6 +297,15 @@ impl<R, F: FnOnce() -> R> FnOnce<()> for AssertUnwindSafe<F> { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<T: fmt::Debug> fmt::Debug for AssertUnwindSafe<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("AssertUnwindSafe") + .field(&self.0) + .finish() + } +} + /// Invokes a closure, capturing the cause of an unwinding panic if one occurs. /// /// This function will return `Ok` with the closure's result if the closure diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 45a10d24528..e5edea241e1 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -177,6 +177,7 @@ pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> { /// panic!("Normal panic"); /// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] +#[derive(Debug)] pub struct PanicInfo<'a> { payload: &'a (Any + Send), location: Location<'a>, @@ -256,6 +257,7 @@ impl<'a> PanicInfo<'a> { /// /// panic!("Normal panic"); /// ``` +#[derive(Debug)] #[stable(feature = "panic_hooks", since = "1.10.0")] pub struct Location<'a> { file: &'a str, diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 858537dd2de..e15c37aaf24 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -114,6 +114,17 @@ impl IntoInner<imp::Process> for Child { fn into_inner(self) -> imp::Process { self.handle } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Child { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Child") + .field("stdin", &self.stdin) + .field("stdout", &self.stdout) + .field("stderr", &self.stderr) + .finish() + } +} + /// A handle to a child process's stdin. This struct is used in the [`stdin`] /// field on [`Child`]. /// @@ -149,6 +160,13 @@ impl FromInner<AnonPipe> for ChildStdin { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for ChildStdin { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("ChildStdin { .. }") + } +} + /// A handle to a child process's stdout. This struct is used in the [`stdout`] /// field on [`Child`]. /// @@ -183,6 +201,13 @@ impl FromInner<AnonPipe> for ChildStdout { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for ChildStdout { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("ChildStdout { .. }") + } +} + /// A handle to a child process's stderr. This struct is used in the [`stderr`] /// field on [`Child`]. /// @@ -217,6 +242,13 @@ impl FromInner<AnonPipe> for ChildStderr { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for ChildStderr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("ChildStderr { .. }") + } +} + /// A process builder, providing fine-grained control /// over how a new process should be spawned. /// @@ -622,6 +654,13 @@ impl FromInner<imp::Stdio> for Stdio { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Stdio { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Stdio { .. }") + } +} + /// Describes the result of a process after it has terminated. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[stable(feature = "process", since = "1.0.0")] @@ -828,6 +867,12 @@ impl Child { /// this function at a known point where there are no more destructors left /// to run. /// +/// ## Platform-specific behavior +/// +/// **Unix**: On Unix-like platforms, it is unlikely that all 32 bits of `exit` +/// will be visible to a parent process inspecting the exit code. On most +/// Unix-like platforms, only the eight least-significant bits are considered. +/// /// # Examples /// /// ``` @@ -835,6 +880,17 @@ impl Child { /// /// process::exit(0); /// ``` +/// +/// Due to [platform-specific behavior], the exit code for this example will be +/// `0` on Linux, but `256` on Windows: +/// +/// ```no_run +/// use std::process; +/// +/// process::exit(0x0f00); +/// ``` +/// +/// [platform-specific behavior]: #platform-specific-behavior #[stable(feature = "rust1", since = "1.0.0")] pub fn exit(code: i32) -> ! { ::sys_common::cleanup(); diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index f48325218fb..b853e83de5d 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -59,6 +59,7 @@ #![unstable(feature = "rand", issue = "0")] use cell::RefCell; +use fmt; use io; use mem; use rc::Rc; @@ -143,6 +144,12 @@ pub struct ThreadRng { rng: Rc<RefCell<ThreadRngInner>>, } +impl fmt::Debug for ThreadRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("ThreadRng { .. }") + } +} + /// Retrieve the lazily-initialized thread-local random number /// generator, seeded by the system. Intended to be used in method /// chaining style, e.g. `thread_rng().gen::<isize>()`. diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs index f46eab68484..b8e83dced8d 100644 --- a/src/libstd/sync/barrier.rs +++ b/src/libstd/sync/barrier.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use fmt; use sync::{Mutex, Condvar}; /// A barrier enables multiple threads to synchronize the beginning @@ -54,6 +55,13 @@ struct BarrierState { #[stable(feature = "rust1", since = "1.0.0")] pub struct BarrierWaitResult(bool); +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Barrier { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Barrier { .. }") + } +} + impl Barrier { /// Creates a new barrier that can block a given number of threads. /// @@ -102,6 +110,15 @@ impl Barrier { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for BarrierWaitResult { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("BarrierWaitResult") + .field("is_leader", &self.is_leader()) + .finish() + } +} + impl BarrierWaitResult { /// Returns whether this thread from `wait` is the "leader thread". /// diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index a983ae716a4..8ab30c51b28 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use fmt; use sync::atomic::{AtomicUsize, Ordering}; use sync::{mutex, MutexGuard, PoisonError}; use sys_common::condvar as sys; @@ -239,6 +240,13 @@ impl Condvar { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Condvar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Condvar { .. }") + } +} + #[stable(feature = "condvar_default", since = "1.9.0")] impl Default for Condvar { /// Creates a `Condvar` which is ready to be waited on and notified. diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index ca6e46eb15a..aeeab170dea 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -306,6 +306,7 @@ impl<T> !Sync for Receiver<T> { } /// whenever `next` is called, waiting for a new message, and `None` will be /// returned when the corresponding channel has hung up. #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct Iter<'a, T: 'a> { rx: &'a Receiver<T> } @@ -316,7 +317,8 @@ pub struct Iter<'a, T: 'a> { /// /// This Iterator will never block the caller in order to wait for data to /// become available. Instead, it will return `None`. -#[unstable(feature = "receiver_try_iter", issue = "34931")] +#[stable(feature = "receiver_try_iter", since = "1.15.0")] +#[derive(Debug)] pub struct TryIter<'a, T: 'a> { rx: &'a Receiver<T> } @@ -325,6 +327,7 @@ pub struct TryIter<'a, T: 'a> { /// whenever `next` is called, waiting for a new message, and `None` will be /// returned when the corresponding channel has hung up. #[stable(feature = "receiver_into_iter", since = "1.1.0")] +#[derive(Debug)] pub struct IntoIter<T> { rx: Receiver<T> } @@ -348,7 +351,7 @@ impl<T> !Sync for Sender<T> { } /// owned by one thread, but it can be cloned to send to other threads. #[stable(feature = "rust1", since = "1.0.0")] pub struct SyncSender<T> { - inner: Arc<UnsafeCell<sync::Packet<T>>>, + inner: Arc<sync::Packet<T>>, } #[stable(feature = "rust1", since = "1.0.0")] @@ -426,10 +429,10 @@ pub enum TrySendError<T> { } enum Flavor<T> { - Oneshot(Arc<UnsafeCell<oneshot::Packet<T>>>), - Stream(Arc<UnsafeCell<stream::Packet<T>>>), - Shared(Arc<UnsafeCell<shared::Packet<T>>>), - Sync(Arc<UnsafeCell<sync::Packet<T>>>), + Oneshot(Arc<oneshot::Packet<T>>), + Stream(Arc<stream::Packet<T>>), + Shared(Arc<shared::Packet<T>>), + Sync(Arc<sync::Packet<T>>), } #[doc(hidden)] @@ -454,10 +457,16 @@ impl<T> UnsafeFlavor<T> for Receiver<T> { } /// Creates a new asynchronous channel, returning the sender/receiver halves. -/// /// All data sent on the sender will become available on the receiver, and no /// send will block the calling thread (this channel has an "infinite buffer"). /// +/// If the [`Receiver`] is disconnected while trying to [`send()`] with the +/// [`Sender`], the [`send()`] method will return an error. +/// +/// [`send()`]: ../../../std/sync/mpsc/struct.Sender.html#method.send +/// [`Sender`]: ../../../std/sync/mpsc/struct.Sender.html +/// [`Receiver`]: ../../../std/sync/mpsc/struct.Receiver.html +/// /// # Examples /// /// ``` @@ -481,24 +490,29 @@ impl<T> UnsafeFlavor<T> for Receiver<T> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn channel<T>() -> (Sender<T>, Receiver<T>) { - let a = Arc::new(UnsafeCell::new(oneshot::Packet::new())); + let a = Arc::new(oneshot::Packet::new()); (Sender::new(Flavor::Oneshot(a.clone())), Receiver::new(Flavor::Oneshot(a))) } /// Creates a new synchronous, bounded channel. /// -/// Like asynchronous channels, the `Receiver` will block until a message +/// Like asynchronous channels, the [`Receiver`] will block until a message /// becomes available. These channels differ greatly in the semantics of the /// sender from asynchronous channels, however. /// -/// This channel has an internal buffer on which messages will be queued. `bound` -/// specifies the buffer size. When the internal buffer becomes full, future sends -/// will *block* waiting for the buffer to open up. Note that a buffer size of 0 -/// is valid, in which case this becomes "rendezvous channel" where each send will -/// not return until a recv is paired with it. +/// This channel has an internal buffer on which messages will be queued. +/// `bound` specifies the buffer size. When the internal buffer becomes full, +/// future sends will *block* waiting for the buffer to open up. Note that a +/// buffer size of 0 is valid, in which case this becomes "rendezvous channel" +/// where each [`send()`] will not return until a recv is paired with it. /// -/// As with asynchronous channels, all senders will panic in `send` if the -/// `Receiver` has been destroyed. +/// Like asynchronous channels, if the [`Receiver`] is disconnected while +/// trying to [`send()`] with the [`SyncSender`], the [`send()`] method will +/// return an error. +/// +/// [`send()`]: ../../../std/sync/mpsc/struct.SyncSender.html#method.send +/// [`SyncSender`]: ../../../std/sync/mpsc/struct.SyncSender.html +/// [`Receiver`]: ../../../std/sync/mpsc/struct.Receiver.html /// /// # Examples /// @@ -521,7 +535,7 @@ pub fn channel<T>() -> (Sender<T>, Receiver<T>) { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn sync_channel<T>(bound: usize) -> (SyncSender<T>, Receiver<T>) { - let a = Arc::new(UnsafeCell::new(sync::Packet::new(bound))); + let a = Arc::new(sync::Packet::new(bound)); (SyncSender::new(a.clone()), Receiver::new(Flavor::Sync(a))) } @@ -567,38 +581,30 @@ impl<T> Sender<T> { pub fn send(&self, t: T) -> Result<(), SendError<T>> { let (new_inner, ret) = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { - unsafe { - let p = p.get(); - if !(*p).sent() { - return (*p).send(t).map_err(SendError); - } else { - let a = - Arc::new(UnsafeCell::new(stream::Packet::new())); - let rx = Receiver::new(Flavor::Stream(a.clone())); - match (*p).upgrade(rx) { - oneshot::UpSuccess => { - let ret = (*a.get()).send(t); - (a, ret) - } - oneshot::UpDisconnected => (a, Err(t)), - oneshot::UpWoke(token) => { - // This send cannot panic because the thread is - // asleep (we're looking at it), so the receiver - // can't go away. - (*a.get()).send(t).ok().unwrap(); - token.signal(); - (a, Ok(())) - } + if !p.sent() { + return p.send(t).map_err(SendError); + } else { + let a = Arc::new(stream::Packet::new()); + let rx = Receiver::new(Flavor::Stream(a.clone())); + match p.upgrade(rx) { + oneshot::UpSuccess => { + let ret = a.send(t); + (a, ret) + } + oneshot::UpDisconnected => (a, Err(t)), + oneshot::UpWoke(token) => { + // This send cannot panic because the thread is + // asleep (we're looking at it), so the receiver + // can't go away. + a.send(t).ok().unwrap(); + token.signal(); + (a, Ok(())) } } } } - Flavor::Stream(ref p) => return unsafe { - (*p.get()).send(t).map_err(SendError) - }, - Flavor::Shared(ref p) => return unsafe { - (*p.get()).send(t).map_err(SendError) - }, + Flavor::Stream(ref p) => return p.send(t).map_err(SendError), + Flavor::Shared(ref p) => return p.send(t).map_err(SendError), Flavor::Sync(..) => unreachable!(), }; @@ -613,41 +619,43 @@ impl<T> Sender<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> Clone for Sender<T> { fn clone(&self) -> Sender<T> { - let (packet, sleeper, guard) = match *unsafe { self.inner() } { + let packet = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { - let a = Arc::new(UnsafeCell::new(shared::Packet::new())); - unsafe { - let guard = (*a.get()).postinit_lock(); + let a = Arc::new(shared::Packet::new()); + { + let guard = a.postinit_lock(); let rx = Receiver::new(Flavor::Shared(a.clone())); - match (*p.get()).upgrade(rx) { + let sleeper = match p.upgrade(rx) { oneshot::UpSuccess | - oneshot::UpDisconnected => (a, None, guard), - oneshot::UpWoke(task) => (a, Some(task), guard) - } + oneshot::UpDisconnected => None, + oneshot::UpWoke(task) => Some(task), + }; + a.inherit_blocker(sleeper, guard); } + a } Flavor::Stream(ref p) => { - let a = Arc::new(UnsafeCell::new(shared::Packet::new())); - unsafe { - let guard = (*a.get()).postinit_lock(); + let a = Arc::new(shared::Packet::new()); + { + let guard = a.postinit_lock(); let rx = Receiver::new(Flavor::Shared(a.clone())); - match (*p.get()).upgrade(rx) { + let sleeper = match p.upgrade(rx) { stream::UpSuccess | - stream::UpDisconnected => (a, None, guard), - stream::UpWoke(task) => (a, Some(task), guard), - } + stream::UpDisconnected => None, + stream::UpWoke(task) => Some(task), + }; + a.inherit_blocker(sleeper, guard); } + a } Flavor::Shared(ref p) => { - unsafe { (*p.get()).clone_chan(); } + p.clone_chan(); return Sender::new(Flavor::Shared(p.clone())); } Flavor::Sync(..) => unreachable!(), }; unsafe { - (*packet.get()).inherit_blocker(sleeper, guard); - let tmp = Sender::new(Flavor::Shared(packet.clone())); mem::swap(self.inner_mut(), tmp.inner_mut()); } @@ -658,10 +666,10 @@ impl<T> Clone for Sender<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> Drop for Sender<T> { fn drop(&mut self) { - match *unsafe { self.inner_mut() } { - Flavor::Oneshot(ref mut p) => unsafe { (*p.get()).drop_chan(); }, - Flavor::Stream(ref mut p) => unsafe { (*p.get()).drop_chan(); }, - Flavor::Shared(ref mut p) => unsafe { (*p.get()).drop_chan(); }, + match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => p.drop_chan(), + Flavor::Stream(ref p) => p.drop_chan(), + Flavor::Shared(ref p) => p.drop_chan(), Flavor::Sync(..) => unreachable!(), } } @@ -679,7 +687,7 @@ impl<T> fmt::Debug for Sender<T> { //////////////////////////////////////////////////////////////////////////////// impl<T> SyncSender<T> { - fn new(inner: Arc<UnsafeCell<sync::Packet<T>>>) -> SyncSender<T> { + fn new(inner: Arc<sync::Packet<T>>) -> SyncSender<T> { SyncSender { inner: inner } } @@ -699,7 +707,7 @@ impl<T> SyncSender<T> { /// information. #[stable(feature = "rust1", since = "1.0.0")] pub fn send(&self, t: T) -> Result<(), SendError<T>> { - unsafe { (*self.inner.get()).send(t).map_err(SendError) } + self.inner.send(t).map_err(SendError) } /// Attempts to send a value on this channel without blocking. @@ -713,14 +721,14 @@ impl<T> SyncSender<T> { /// receiver has received the data or not if this function is successful. #[stable(feature = "rust1", since = "1.0.0")] pub fn try_send(&self, t: T) -> Result<(), TrySendError<T>> { - unsafe { (*self.inner.get()).try_send(t) } + self.inner.try_send(t) } } #[stable(feature = "rust1", since = "1.0.0")] impl<T> Clone for SyncSender<T> { fn clone(&self) -> SyncSender<T> { - unsafe { (*self.inner.get()).clone_chan(); } + self.inner.clone_chan(); SyncSender::new(self.inner.clone()) } } @@ -728,7 +736,7 @@ impl<T> Clone for SyncSender<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> Drop for SyncSender<T> { fn drop(&mut self) { - unsafe { (*self.inner.get()).drop_chan(); } + self.inner.drop_chan(); } } @@ -761,7 +769,7 @@ impl<T> Receiver<T> { loop { let new_port = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { - match unsafe { (*p.get()).try_recv() } { + match p.try_recv() { Ok(t) => return Ok(t), Err(oneshot::Empty) => return Err(TryRecvError::Empty), Err(oneshot::Disconnected) => { @@ -771,7 +779,7 @@ impl<T> Receiver<T> { } } Flavor::Stream(ref p) => { - match unsafe { (*p.get()).try_recv() } { + match p.try_recv() { Ok(t) => return Ok(t), Err(stream::Empty) => return Err(TryRecvError::Empty), Err(stream::Disconnected) => { @@ -781,7 +789,7 @@ impl<T> Receiver<T> { } } Flavor::Shared(ref p) => { - match unsafe { (*p.get()).try_recv() } { + match p.try_recv() { Ok(t) => return Ok(t), Err(shared::Empty) => return Err(TryRecvError::Empty), Err(shared::Disconnected) => { @@ -790,7 +798,7 @@ impl<T> Receiver<T> { } } Flavor::Sync(ref p) => { - match unsafe { (*p.get()).try_recv() } { + match p.try_recv() { Ok(t) => return Ok(t), Err(sync::Empty) => return Err(TryRecvError::Empty), Err(sync::Disconnected) => { @@ -864,7 +872,7 @@ impl<T> Receiver<T> { loop { let new_port = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { - match unsafe { (*p.get()).recv(None) } { + match p.recv(None) { Ok(t) => return Ok(t), Err(oneshot::Disconnected) => return Err(RecvError), Err(oneshot::Upgraded(rx)) => rx, @@ -872,7 +880,7 @@ impl<T> Receiver<T> { } } Flavor::Stream(ref p) => { - match unsafe { (*p.get()).recv(None) } { + match p.recv(None) { Ok(t) => return Ok(t), Err(stream::Disconnected) => return Err(RecvError), Err(stream::Upgraded(rx)) => rx, @@ -880,15 +888,13 @@ impl<T> Receiver<T> { } } Flavor::Shared(ref p) => { - match unsafe { (*p.get()).recv(None) } { + match p.recv(None) { Ok(t) => return Ok(t), Err(shared::Disconnected) => return Err(RecvError), Err(shared::Empty) => unreachable!(), } } - Flavor::Sync(ref p) => return unsafe { - (*p.get()).recv(None).map_err(|_| RecvError) - } + Flavor::Sync(ref p) => return p.recv(None).map_err(|_| RecvError), }; unsafe { mem::swap(self.inner_mut(), new_port.inner_mut()); @@ -941,7 +947,7 @@ impl<T> Receiver<T> { loop { let port_or_empty = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { - match unsafe { (*p.get()).recv(Some(deadline)) } { + match p.recv(Some(deadline)) { Ok(t) => return Ok(t), Err(oneshot::Disconnected) => return Err(Disconnected), Err(oneshot::Upgraded(rx)) => Some(rx), @@ -949,7 +955,7 @@ impl<T> Receiver<T> { } } Flavor::Stream(ref p) => { - match unsafe { (*p.get()).recv(Some(deadline)) } { + match p.recv(Some(deadline)) { Ok(t) => return Ok(t), Err(stream::Disconnected) => return Err(Disconnected), Err(stream::Upgraded(rx)) => Some(rx), @@ -957,14 +963,14 @@ impl<T> Receiver<T> { } } Flavor::Shared(ref p) => { - match unsafe { (*p.get()).recv(Some(deadline)) } { + match p.recv(Some(deadline)) { Ok(t) => return Ok(t), Err(shared::Disconnected) => return Err(Disconnected), Err(shared::Empty) => None, } } Flavor::Sync(ref p) => { - match unsafe { (*p.get()).recv(Some(deadline)) } { + match p.recv(Some(deadline)) { Ok(t) => return Ok(t), Err(sync::Disconnected) => return Err(Disconnected), Err(sync::Empty) => None, @@ -997,7 +1003,7 @@ impl<T> Receiver<T> { /// It will return `None` if there are no more pending values or if the /// channel has hung up. The iterator will never `panic!` or block the /// user by waiting for values. - #[unstable(feature = "receiver_try_iter", issue = "34931")] + #[stable(feature = "receiver_try_iter", since = "1.15.0")] pub fn try_iter(&self) -> TryIter<T> { TryIter { rx: self } } @@ -1009,23 +1015,19 @@ impl<T> select::Packet for Receiver<T> { loop { let new_port = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { - match unsafe { (*p.get()).can_recv() } { + match p.can_recv() { Ok(ret) => return ret, Err(upgrade) => upgrade, } } Flavor::Stream(ref p) => { - match unsafe { (*p.get()).can_recv() } { + match p.can_recv() { Ok(ret) => return ret, Err(upgrade) => upgrade, } } - Flavor::Shared(ref p) => { - return unsafe { (*p.get()).can_recv() }; - } - Flavor::Sync(ref p) => { - return unsafe { (*p.get()).can_recv() }; - } + Flavor::Shared(ref p) => return p.can_recv(), + Flavor::Sync(ref p) => return p.can_recv(), }; unsafe { mem::swap(self.inner_mut(), @@ -1038,25 +1040,21 @@ impl<T> select::Packet for Receiver<T> { loop { let (t, new_port) = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { - match unsafe { (*p.get()).start_selection(token) } { + match p.start_selection(token) { oneshot::SelSuccess => return Installed, oneshot::SelCanceled => return Abort, oneshot::SelUpgraded(t, rx) => (t, rx), } } Flavor::Stream(ref p) => { - match unsafe { (*p.get()).start_selection(token) } { + match p.start_selection(token) { stream::SelSuccess => return Installed, stream::SelCanceled => return Abort, stream::SelUpgraded(t, rx) => (t, rx), } } - Flavor::Shared(ref p) => { - return unsafe { (*p.get()).start_selection(token) }; - } - Flavor::Sync(ref p) => { - return unsafe { (*p.get()).start_selection(token) }; - } + Flavor::Shared(ref p) => return p.start_selection(token), + Flavor::Sync(ref p) => return p.start_selection(token), }; token = t; unsafe { @@ -1069,16 +1067,10 @@ impl<T> select::Packet for Receiver<T> { let mut was_upgrade = false; loop { let result = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => unsafe { (*p.get()).abort_selection() }, - Flavor::Stream(ref p) => unsafe { - (*p.get()).abort_selection(was_upgrade) - }, - Flavor::Shared(ref p) => return unsafe { - (*p.get()).abort_selection(was_upgrade) - }, - Flavor::Sync(ref p) => return unsafe { - (*p.get()).abort_selection() - }, + Flavor::Oneshot(ref p) => p.abort_selection(), + Flavor::Stream(ref p) => p.abort_selection(was_upgrade), + Flavor::Shared(ref p) => return p.abort_selection(was_upgrade), + Flavor::Sync(ref p) => return p.abort_selection(), }; let new_port = match result { Ok(b) => return b, Err(p) => p }; was_upgrade = true; @@ -1097,7 +1089,7 @@ impl<'a, T> Iterator for Iter<'a, T> { fn next(&mut self) -> Option<T> { self.rx.recv().ok() } } -#[unstable(feature = "receiver_try_iter", issue = "34931")] +#[stable(feature = "receiver_try_iter", since = "1.15.0")] impl<'a, T> Iterator for TryIter<'a, T> { type Item = T; @@ -1131,11 +1123,11 @@ impl <T> IntoIterator for Receiver<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> Drop for Receiver<T> { fn drop(&mut self) { - match *unsafe { self.inner_mut() } { - Flavor::Oneshot(ref mut p) => unsafe { (*p.get()).drop_port(); }, - Flavor::Stream(ref mut p) => unsafe { (*p.get()).drop_port(); }, - Flavor::Shared(ref mut p) => unsafe { (*p.get()).drop_port(); }, - Flavor::Sync(ref mut p) => unsafe { (*p.get()).drop_port(); }, + match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => p.drop_port(), + Flavor::Stream(ref p) => p.drop_port(), + Flavor::Shared(ref p) => p.drop_port(), + Flavor::Sync(ref p) => p.drop_port(), } } } diff --git a/src/libstd/sync/mpsc/oneshot.rs b/src/libstd/sync/mpsc/oneshot.rs index 767e9f96ac8..b8e50c9297b 100644 --- a/src/libstd/sync/mpsc/oneshot.rs +++ b/src/libstd/sync/mpsc/oneshot.rs @@ -39,7 +39,8 @@ use self::MyUpgrade::*; use sync::mpsc::Receiver; use sync::mpsc::blocking::{self, SignalToken}; -use core::mem; +use cell::UnsafeCell; +use ptr; use sync::atomic::{AtomicUsize, Ordering}; use time::Instant; @@ -57,10 +58,10 @@ pub struct Packet<T> { // Internal state of the chan/port pair (stores the blocked thread as well) state: AtomicUsize, // One-shot data slot location - data: Option<T>, + data: UnsafeCell<Option<T>>, // when used for the second time, a oneshot channel must be upgraded, and // this contains the slot for the upgrade - upgrade: MyUpgrade<T>, + upgrade: UnsafeCell<MyUpgrade<T>>, } pub enum Failure<T> { @@ -90,42 +91,44 @@ enum MyUpgrade<T> { impl<T> Packet<T> { pub fn new() -> Packet<T> { Packet { - data: None, - upgrade: NothingSent, + data: UnsafeCell::new(None), + upgrade: UnsafeCell::new(NothingSent), state: AtomicUsize::new(EMPTY), } } - pub fn send(&mut self, t: T) -> Result<(), T> { - // Sanity check - match self.upgrade { - NothingSent => {} - _ => panic!("sending on a oneshot that's already sent on "), - } - assert!(self.data.is_none()); - self.data = Some(t); - self.upgrade = SendUsed; - - match self.state.swap(DATA, Ordering::SeqCst) { - // Sent the data, no one was waiting - EMPTY => Ok(()), - - // Couldn't send the data, the port hung up first. Return the data - // back up the stack. - DISCONNECTED => { - self.state.swap(DISCONNECTED, Ordering::SeqCst); - self.upgrade = NothingSent; - Err(self.data.take().unwrap()) + pub fn send(&self, t: T) -> Result<(), T> { + unsafe { + // Sanity check + match *self.upgrade.get() { + NothingSent => {} + _ => panic!("sending on a oneshot that's already sent on "), } + assert!((*self.data.get()).is_none()); + ptr::write(self.data.get(), Some(t)); + ptr::write(self.upgrade.get(), SendUsed); + + match self.state.swap(DATA, Ordering::SeqCst) { + // Sent the data, no one was waiting + EMPTY => Ok(()), + + // Couldn't send the data, the port hung up first. Return the data + // back up the stack. + DISCONNECTED => { + self.state.swap(DISCONNECTED, Ordering::SeqCst); + ptr::write(self.upgrade.get(), NothingSent); + Err((&mut *self.data.get()).take().unwrap()) + } - // Not possible, these are one-use channels - DATA => unreachable!(), + // Not possible, these are one-use channels + DATA => unreachable!(), - // There is a thread waiting on the other end. We leave the 'DATA' - // state inside so it'll pick it up on the other end. - ptr => unsafe { - SignalToken::cast_from_usize(ptr).signal(); - Ok(()) + // There is a thread waiting on the other end. We leave the 'DATA' + // state inside so it'll pick it up on the other end. + ptr => { + SignalToken::cast_from_usize(ptr).signal(); + Ok(()) + } } } } @@ -133,13 +136,15 @@ impl<T> Packet<T> { // Just tests whether this channel has been sent on or not, this is only // safe to use from the sender. pub fn sent(&self) -> bool { - match self.upgrade { - NothingSent => false, - _ => true, + unsafe { + match *self.upgrade.get() { + NothingSent => false, + _ => true, + } } } - pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure<T>> { + pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure<T>> { // Attempt to not block the thread (it's a little expensive). If it looks // like we're not empty, then immediately go through to `try_recv`. if self.state.load(Ordering::SeqCst) == EMPTY { @@ -167,73 +172,77 @@ impl<T> Packet<T> { self.try_recv() } - pub fn try_recv(&mut self) -> Result<T, Failure<T>> { - match self.state.load(Ordering::SeqCst) { - EMPTY => Err(Empty), - - // We saw some data on the channel, but the channel can be used - // again to send us an upgrade. As a result, we need to re-insert - // into the channel that there's no data available (otherwise we'll - // just see DATA next time). This is done as a cmpxchg because if - // the state changes under our feet we'd rather just see that state - // change. - DATA => { - self.state.compare_and_swap(DATA, EMPTY, Ordering::SeqCst); - match self.data.take() { - Some(data) => Ok(data), - None => unreachable!(), + pub fn try_recv(&self) -> Result<T, Failure<T>> { + unsafe { + match self.state.load(Ordering::SeqCst) { + EMPTY => Err(Empty), + + // We saw some data on the channel, but the channel can be used + // again to send us an upgrade. As a result, we need to re-insert + // into the channel that there's no data available (otherwise we'll + // just see DATA next time). This is done as a cmpxchg because if + // the state changes under our feet we'd rather just see that state + // change. + DATA => { + self.state.compare_and_swap(DATA, EMPTY, Ordering::SeqCst); + match (&mut *self.data.get()).take() { + Some(data) => Ok(data), + None => unreachable!(), + } } - } - // There's no guarantee that we receive before an upgrade happens, - // and an upgrade flags the channel as disconnected, so when we see - // this we first need to check if there's data available and *then* - // we go through and process the upgrade. - DISCONNECTED => { - match self.data.take() { - Some(data) => Ok(data), - None => { - match mem::replace(&mut self.upgrade, SendUsed) { - SendUsed | NothingSent => Err(Disconnected), - GoUp(upgrade) => Err(Upgraded(upgrade)) + // There's no guarantee that we receive before an upgrade happens, + // and an upgrade flags the channel as disconnected, so when we see + // this we first need to check if there's data available and *then* + // we go through and process the upgrade. + DISCONNECTED => { + match (&mut *self.data.get()).take() { + Some(data) => Ok(data), + None => { + match ptr::replace(self.upgrade.get(), SendUsed) { + SendUsed | NothingSent => Err(Disconnected), + GoUp(upgrade) => Err(Upgraded(upgrade)) + } } } } - } - // We are the sole receiver; there cannot be a blocking - // receiver already. - _ => unreachable!() + // We are the sole receiver; there cannot be a blocking + // receiver already. + _ => unreachable!() + } } } // Returns whether the upgrade was completed. If the upgrade wasn't // completed, then the port couldn't get sent to the other half (it will // never receive it). - pub fn upgrade(&mut self, up: Receiver<T>) -> UpgradeResult { - let prev = match self.upgrade { - NothingSent => NothingSent, - SendUsed => SendUsed, - _ => panic!("upgrading again"), - }; - self.upgrade = GoUp(up); - - match self.state.swap(DISCONNECTED, Ordering::SeqCst) { - // If the channel is empty or has data on it, then we're good to go. - // Senders will check the data before the upgrade (in case we - // plastered over the DATA state). - DATA | EMPTY => UpSuccess, - - // If the other end is already disconnected, then we failed the - // upgrade. Be sure to trash the port we were given. - DISCONNECTED => { self.upgrade = prev; UpDisconnected } - - // If someone's waiting, we gotta wake them up - ptr => UpWoke(unsafe { SignalToken::cast_from_usize(ptr) }) + pub fn upgrade(&self, up: Receiver<T>) -> UpgradeResult { + unsafe { + let prev = match *self.upgrade.get() { + NothingSent => NothingSent, + SendUsed => SendUsed, + _ => panic!("upgrading again"), + }; + ptr::write(self.upgrade.get(), GoUp(up)); + + match self.state.swap(DISCONNECTED, Ordering::SeqCst) { + // If the channel is empty or has data on it, then we're good to go. + // Senders will check the data before the upgrade (in case we + // plastered over the DATA state). + DATA | EMPTY => UpSuccess, + + // If the other end is already disconnected, then we failed the + // upgrade. Be sure to trash the port we were given. + DISCONNECTED => { ptr::replace(self.upgrade.get(), prev); UpDisconnected } + + // If someone's waiting, we gotta wake them up + ptr => UpWoke(SignalToken::cast_from_usize(ptr)) + } } } - pub fn drop_chan(&mut self) { + pub fn drop_chan(&self) { match self.state.swap(DISCONNECTED, Ordering::SeqCst) { DATA | DISCONNECTED | EMPTY => {} @@ -244,7 +253,7 @@ impl<T> Packet<T> { } } - pub fn drop_port(&mut self) { + pub fn drop_port(&self) { match self.state.swap(DISCONNECTED, Ordering::SeqCst) { // An empty channel has nothing to do, and a remotely disconnected // channel also has nothing to do b/c we're about to run the drop @@ -254,7 +263,7 @@ impl<T> Packet<T> { // There's data on the channel, so make sure we destroy it promptly. // This is why not using an arc is a little difficult (need the box // to stay valid while we take the data). - DATA => { self.data.take().unwrap(); } + DATA => unsafe { (&mut *self.data.get()).take().unwrap(); }, // We're the only ones that can block on this port _ => unreachable!() @@ -267,62 +276,66 @@ impl<T> Packet<T> { // If Ok, the value is whether this port has data, if Err, then the upgraded // port needs to be checked instead of this one. - pub fn can_recv(&mut self) -> Result<bool, Receiver<T>> { - match self.state.load(Ordering::SeqCst) { - EMPTY => Ok(false), // Welp, we tried - DATA => Ok(true), // we have some un-acquired data - DISCONNECTED if self.data.is_some() => Ok(true), // we have data - DISCONNECTED => { - match mem::replace(&mut self.upgrade, SendUsed) { - // The other end sent us an upgrade, so we need to - // propagate upwards whether the upgrade can receive - // data - GoUp(upgrade) => Err(upgrade), - - // If the other end disconnected without sending an - // upgrade, then we have data to receive (the channel is - // disconnected). - up => { self.upgrade = up; Ok(true) } + pub fn can_recv(&self) -> Result<bool, Receiver<T>> { + unsafe { + match self.state.load(Ordering::SeqCst) { + EMPTY => Ok(false), // Welp, we tried + DATA => Ok(true), // we have some un-acquired data + DISCONNECTED if (*self.data.get()).is_some() => Ok(true), // we have data + DISCONNECTED => { + match ptr::replace(self.upgrade.get(), SendUsed) { + // The other end sent us an upgrade, so we need to + // propagate upwards whether the upgrade can receive + // data + GoUp(upgrade) => Err(upgrade), + + // If the other end disconnected without sending an + // upgrade, then we have data to receive (the channel is + // disconnected). + up => { ptr::write(self.upgrade.get(), up); Ok(true) } + } } + _ => unreachable!(), // we're the "one blocker" } - _ => unreachable!(), // we're the "one blocker" } } // Attempts to start selection on this port. This can either succeed, fail // because there is data, or fail because there is an upgrade pending. - pub fn start_selection(&mut self, token: SignalToken) -> SelectionResult<T> { - let ptr = unsafe { token.cast_to_usize() }; - match self.state.compare_and_swap(EMPTY, ptr, Ordering::SeqCst) { - EMPTY => SelSuccess, - DATA => { - drop(unsafe { SignalToken::cast_from_usize(ptr) }); - SelCanceled - } - DISCONNECTED if self.data.is_some() => { - drop(unsafe { SignalToken::cast_from_usize(ptr) }); - SelCanceled - } - DISCONNECTED => { - match mem::replace(&mut self.upgrade, SendUsed) { - // The other end sent us an upgrade, so we need to - // propagate upwards whether the upgrade can receive - // data - GoUp(upgrade) => { - SelUpgraded(unsafe { SignalToken::cast_from_usize(ptr) }, upgrade) - } + pub fn start_selection(&self, token: SignalToken) -> SelectionResult<T> { + unsafe { + let ptr = token.cast_to_usize(); + match self.state.compare_and_swap(EMPTY, ptr, Ordering::SeqCst) { + EMPTY => SelSuccess, + DATA => { + drop(SignalToken::cast_from_usize(ptr)); + SelCanceled + } + DISCONNECTED if (*self.data.get()).is_some() => { + drop(SignalToken::cast_from_usize(ptr)); + SelCanceled + } + DISCONNECTED => { + match ptr::replace(self.upgrade.get(), SendUsed) { + // The other end sent us an upgrade, so we need to + // propagate upwards whether the upgrade can receive + // data + GoUp(upgrade) => { + SelUpgraded(SignalToken::cast_from_usize(ptr), upgrade) + } - // If the other end disconnected without sending an - // upgrade, then we have data to receive (the channel is - // disconnected). - up => { - self.upgrade = up; - drop(unsafe { SignalToken::cast_from_usize(ptr) }); - SelCanceled + // If the other end disconnected without sending an + // upgrade, then we have data to receive (the channel is + // disconnected). + up => { + ptr::write(self.upgrade.get(), up); + drop(SignalToken::cast_from_usize(ptr)); + SelCanceled + } } } + _ => unreachable!(), // we're the "one blocker" } - _ => unreachable!(), // we're the "one blocker" } } @@ -330,7 +343,7 @@ impl<T> Packet<T> { // blocked thread will no longer be visible to any other threads. // // The return value indicates whether there's data on this port. - pub fn abort_selection(&mut self) -> Result<bool, Receiver<T>> { + pub fn abort_selection(&self) -> Result<bool, Receiver<T>> { let state = match self.state.load(Ordering::SeqCst) { // Each of these states means that no further activity will happen // with regard to abortion selection @@ -356,16 +369,16 @@ impl<T> Packet<T> { // // We then need to check to see if there was an upgrade requested, // and if so, the upgraded port needs to have its selection aborted. - DISCONNECTED => { - if self.data.is_some() { + DISCONNECTED => unsafe { + if (*self.data.get()).is_some() { Ok(true) } else { - match mem::replace(&mut self.upgrade, SendUsed) { + match ptr::replace(self.upgrade.get(), SendUsed) { GoUp(port) => Err(port), _ => Ok(true), } } - } + }, // We woke ourselves up from select. ptr => unsafe { diff --git a/src/libstd/sync/mpsc/shared.rs b/src/libstd/sync/mpsc/shared.rs index 2a9618251ff..f9e02904164 100644 --- a/src/libstd/sync/mpsc/shared.rs +++ b/src/libstd/sync/mpsc/shared.rs @@ -24,6 +24,8 @@ use core::cmp; use core::intrinsics::abort; use core::isize; +use cell::UnsafeCell; +use ptr; use sync::atomic::{AtomicUsize, AtomicIsize, AtomicBool, Ordering}; use sync::mpsc::blocking::{self, SignalToken}; use sync::mpsc::mpsc_queue as mpsc; @@ -44,7 +46,7 @@ const MAX_STEALS: isize = 1 << 20; pub struct Packet<T> { queue: mpsc::Queue<T>, cnt: AtomicIsize, // How many items are on this channel - steals: isize, // How many times has a port received without blocking? + steals: UnsafeCell<isize>, // How many times has a port received without blocking? to_wake: AtomicUsize, // SignalToken for wake up // The number of channels which are currently using this packet. @@ -72,7 +74,7 @@ impl<T> Packet<T> { Packet { queue: mpsc::Queue::new(), cnt: AtomicIsize::new(0), - steals: 0, + steals: UnsafeCell::new(0), to_wake: AtomicUsize::new(0), channels: AtomicUsize::new(2), port_dropped: AtomicBool::new(false), @@ -95,7 +97,7 @@ impl<T> Packet<T> { // threads in select(). // // This can only be called at channel-creation time - pub fn inherit_blocker(&mut self, + pub fn inherit_blocker(&self, token: Option<SignalToken>, guard: MutexGuard<()>) { token.map(|token| { @@ -122,7 +124,7 @@ impl<T> Packet<T> { // To offset this bad increment, we initially set the steal count to // -1. You'll find some special code in abort_selection() as well to // ensure that this -1 steal count doesn't escape too far. - self.steals = -1; + unsafe { *self.steals.get() = -1; } }); // When the shared packet is constructed, we grabbed this lock. The @@ -133,7 +135,7 @@ impl<T> Packet<T> { drop(guard); } - pub fn send(&mut self, t: T) -> Result<(), T> { + pub fn send(&self, t: T) -> Result<(), T> { // See Port::drop for what's going on if self.port_dropped.load(Ordering::SeqCst) { return Err(t) } @@ -218,7 +220,7 @@ impl<T> Packet<T> { Ok(()) } - pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure> { + pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure> { // This code is essentially the exact same as that found in the stream // case (see stream.rs) match self.try_recv() { @@ -239,37 +241,38 @@ impl<T> Packet<T> { } match self.try_recv() { - data @ Ok(..) => { self.steals -= 1; data } + data @ Ok(..) => unsafe { *self.steals.get() -= 1; data }, data => data, } } // Essentially the exact same thing as the stream decrement function. // Returns true if blocking should proceed. - fn decrement(&mut self, token: SignalToken) -> StartResult { - assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); - let ptr = unsafe { token.cast_to_usize() }; - self.to_wake.store(ptr, Ordering::SeqCst); - - let steals = self.steals; - self.steals = 0; - - match self.cnt.fetch_sub(1 + steals, Ordering::SeqCst) { - DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); } - // If we factor in our steals and notice that the channel has no - // data, we successfully sleep - n => { - assert!(n >= 0); - if n - steals <= 0 { return Installed } + fn decrement(&self, token: SignalToken) -> StartResult { + unsafe { + assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); + let ptr = token.cast_to_usize(); + self.to_wake.store(ptr, Ordering::SeqCst); + + let steals = ptr::replace(self.steals.get(), 0); + + match self.cnt.fetch_sub(1 + steals, Ordering::SeqCst) { + DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); } + // If we factor in our steals and notice that the channel has no + // data, we successfully sleep + n => { + assert!(n >= 0); + if n - steals <= 0 { return Installed } + } } - } - self.to_wake.store(0, Ordering::SeqCst); - drop(unsafe { SignalToken::cast_from_usize(ptr) }); - Abort + self.to_wake.store(0, Ordering::SeqCst); + drop(SignalToken::cast_from_usize(ptr)); + Abort + } } - pub fn try_recv(&mut self) -> Result<T, Failure> { + pub fn try_recv(&self) -> Result<T, Failure> { let ret = match self.queue.pop() { mpsc::Data(t) => Some(t), mpsc::Empty => None, @@ -303,23 +306,23 @@ impl<T> Packet<T> { match ret { // See the discussion in the stream implementation for why we // might decrement steals. - Some(data) => { - if self.steals > MAX_STEALS { + Some(data) => unsafe { + if *self.steals.get() > MAX_STEALS { match self.cnt.swap(0, Ordering::SeqCst) { DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); } n => { - let m = cmp::min(n, self.steals); - self.steals -= m; + let m = cmp::min(n, *self.steals.get()); + *self.steals.get() -= m; self.bump(n - m); } } - assert!(self.steals >= 0); + assert!(*self.steals.get() >= 0); } - self.steals += 1; + *self.steals.get() += 1; Ok(data) - } + }, // See the discussion in the stream implementation for why we try // again. @@ -341,7 +344,7 @@ impl<T> Packet<T> { // Prepares this shared packet for a channel clone, essentially just bumping // a refcount. - pub fn clone_chan(&mut self) { + pub fn clone_chan(&self) { let old_count = self.channels.fetch_add(1, Ordering::SeqCst); // See comments on Arc::clone() on why we do this (for `mem::forget`). @@ -355,7 +358,7 @@ impl<T> Packet<T> { // Decrement the reference count on a channel. This is called whenever a // Chan is dropped and may end up waking up a receiver. It's the receiver's // responsibility on the other end to figure out that we've disconnected. - pub fn drop_chan(&mut self) { + pub fn drop_chan(&self) { match self.channels.fetch_sub(1, Ordering::SeqCst) { 1 => {} n if n > 1 => return, @@ -371,9 +374,9 @@ impl<T> Packet<T> { // See the long discussion inside of stream.rs for why the queue is drained, // and why it is done in this fashion. - pub fn drop_port(&mut self) { + pub fn drop_port(&self) { self.port_dropped.store(true, Ordering::SeqCst); - let mut steals = self.steals; + let mut steals = unsafe { *self.steals.get() }; while { let cnt = self.cnt.compare_and_swap(steals, DISCONNECTED, Ordering::SeqCst); cnt != DISCONNECTED && cnt != steals @@ -390,7 +393,7 @@ impl<T> Packet<T> { } // Consumes ownership of the 'to_wake' field. - fn take_to_wake(&mut self) -> SignalToken { + fn take_to_wake(&self) -> SignalToken { let ptr = self.to_wake.load(Ordering::SeqCst); self.to_wake.store(0, Ordering::SeqCst); assert!(ptr != 0); @@ -406,13 +409,13 @@ impl<T> Packet<T> { // // This is different than the stream version because there's no need to peek // at the queue, we can just look at the local count. - pub fn can_recv(&mut self) -> bool { + pub fn can_recv(&self) -> bool { let cnt = self.cnt.load(Ordering::SeqCst); - cnt == DISCONNECTED || cnt - self.steals > 0 + cnt == DISCONNECTED || cnt - unsafe { *self.steals.get() } > 0 } // increment the count on the channel (used for selection) - fn bump(&mut self, amt: isize) -> isize { + fn bump(&self, amt: isize) -> isize { match self.cnt.fetch_add(amt, Ordering::SeqCst) { DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); @@ -427,7 +430,7 @@ impl<T> Packet<T> { // // The code here is the same as in stream.rs, except that it doesn't need to // peek at the channel to see if an upgrade is pending. - pub fn start_selection(&mut self, token: SignalToken) -> StartResult { + pub fn start_selection(&self, token: SignalToken) -> StartResult { match self.decrement(token) { Installed => Installed, Abort => { @@ -443,7 +446,7 @@ impl<T> Packet<T> { // // This is similar to the stream implementation (hence fewer comments), but // uses a different value for the "steals" variable. - pub fn abort_selection(&mut self, _was_upgrade: bool) -> bool { + pub fn abort_selection(&self, _was_upgrade: bool) -> bool { // Before we do anything else, we bounce on this lock. The reason for // doing this is to ensure that any upgrade-in-progress is gone and // done with. Without this bounce, we can race with inherit_blocker @@ -477,12 +480,15 @@ impl<T> Packet<T> { thread::yield_now(); } } - // if the number of steals is -1, it was the pre-emptive -1 steal - // count from when we inherited a blocker. This is fine because - // we're just going to overwrite it with a real value. - assert!(self.steals == 0 || self.steals == -1); - self.steals = steals; - prev >= 0 + unsafe { + // if the number of steals is -1, it was the pre-emptive -1 steal + // count from when we inherited a blocker. This is fine because + // we're just going to overwrite it with a real value. + let old = self.steals.get(); + assert!(*old == 0 || *old == -1); + *old = steals; + prev >= 0 + } } } } diff --git a/src/libstd/sync/mpsc/stream.rs b/src/libstd/sync/mpsc/stream.rs index 61c8316467d..47cd8977fda 100644 --- a/src/libstd/sync/mpsc/stream.rs +++ b/src/libstd/sync/mpsc/stream.rs @@ -22,8 +22,10 @@ pub use self::UpgradeResult::*; pub use self::SelectionResult::*; use self::Message::*; +use cell::UnsafeCell; use core::cmp; use core::isize; +use ptr; use thread; use time::Instant; @@ -42,7 +44,7 @@ pub struct Packet<T> { queue: spsc::Queue<Message<T>>, // internal queue for all message cnt: AtomicIsize, // How many items are on this channel - steals: isize, // How many times has a port received without blocking? + steals: UnsafeCell<isize>, // How many times has a port received without blocking? to_wake: AtomicUsize, // SignalToken for the blocked thread to wake up port_dropped: AtomicBool, // flag if the channel has been destroyed. @@ -79,14 +81,14 @@ impl<T> Packet<T> { queue: unsafe { spsc::Queue::new(128) }, cnt: AtomicIsize::new(0), - steals: 0, + steals: UnsafeCell::new(0), to_wake: AtomicUsize::new(0), port_dropped: AtomicBool::new(false), } } - pub fn send(&mut self, t: T) -> Result<(), T> { + pub fn send(&self, t: T) -> Result<(), T> { // If the other port has deterministically gone away, then definitely // must return the data back up the stack. Otherwise, the data is // considered as being sent. @@ -99,7 +101,7 @@ impl<T> Packet<T> { Ok(()) } - pub fn upgrade(&mut self, up: Receiver<T>) -> UpgradeResult { + pub fn upgrade(&self, up: Receiver<T>) -> UpgradeResult { // If the port has gone away, then there's no need to proceed any // further. if self.port_dropped.load(Ordering::SeqCst) { return UpDisconnected } @@ -107,7 +109,7 @@ impl<T> Packet<T> { self.do_send(GoUp(up)) } - fn do_send(&mut self, t: Message<T>) -> UpgradeResult { + fn do_send(&self, t: Message<T>) -> UpgradeResult { self.queue.push(t); match self.cnt.fetch_add(1, Ordering::SeqCst) { // As described in the mod's doc comment, -1 == wakeup @@ -141,7 +143,7 @@ impl<T> Packet<T> { } // Consumes ownership of the 'to_wake' field. - fn take_to_wake(&mut self) -> SignalToken { + fn take_to_wake(&self) -> SignalToken { let ptr = self.to_wake.load(Ordering::SeqCst); self.to_wake.store(0, Ordering::SeqCst); assert!(ptr != 0); @@ -151,13 +153,12 @@ impl<T> Packet<T> { // Decrements the count on the channel for a sleeper, returning the sleeper // back if it shouldn't sleep. Note that this is the location where we take // steals into account. - fn decrement(&mut self, token: SignalToken) -> Result<(), SignalToken> { + fn decrement(&self, token: SignalToken) -> Result<(), SignalToken> { assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); let ptr = unsafe { token.cast_to_usize() }; self.to_wake.store(ptr, Ordering::SeqCst); - let steals = self.steals; - self.steals = 0; + let steals = unsafe { ptr::replace(self.steals.get(), 0) }; match self.cnt.fetch_sub(1 + steals, Ordering::SeqCst) { DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); } @@ -173,7 +174,7 @@ impl<T> Packet<T> { Err(unsafe { SignalToken::cast_from_usize(ptr) }) } - pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure<T>> { + pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure<T>> { // Optimistic preflight check (scheduling is expensive). match self.try_recv() { Err(Empty) => {} @@ -199,16 +200,16 @@ impl<T> Packet<T> { // a steal, so offset the decrement here (we already have our // "steal" factored into the channel count above). data @ Ok(..) | - data @ Err(Upgraded(..)) => { - self.steals -= 1; + data @ Err(Upgraded(..)) => unsafe { + *self.steals.get() -= 1; data - } + }, data => data, } } - pub fn try_recv(&mut self) -> Result<T, Failure<T>> { + pub fn try_recv(&self) -> Result<T, Failure<T>> { match self.queue.pop() { // If we stole some data, record to that effect (this will be // factored into cnt later on). @@ -221,26 +222,26 @@ impl<T> Packet<T> { // a pretty slow operation, of swapping 0 into cnt, taking steals // down as much as possible (without going negative), and then // adding back in whatever we couldn't factor into steals. - Some(data) => { - if self.steals > MAX_STEALS { + Some(data) => unsafe { + if *self.steals.get() > MAX_STEALS { match self.cnt.swap(0, Ordering::SeqCst) { DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); } n => { - let m = cmp::min(n, self.steals); - self.steals -= m; + let m = cmp::min(n, *self.steals.get()); + *self.steals.get() -= m; self.bump(n - m); } } - assert!(self.steals >= 0); + assert!(*self.steals.get() >= 0); } - self.steals += 1; + *self.steals.get() += 1; match data { Data(t) => Ok(t), GoUp(up) => Err(Upgraded(up)), } - } + }, None => { match self.cnt.load(Ordering::SeqCst) { @@ -269,7 +270,7 @@ impl<T> Packet<T> { } } - pub fn drop_chan(&mut self) { + pub fn drop_chan(&self) { // Dropping a channel is pretty simple, we just flag it as disconnected // and then wakeup a blocker if there is one. match self.cnt.swap(DISCONNECTED, Ordering::SeqCst) { @@ -279,7 +280,7 @@ impl<T> Packet<T> { } } - pub fn drop_port(&mut self) { + pub fn drop_port(&self) { // Dropping a port seems like a fairly trivial thing. In theory all we // need to do is flag that we're disconnected and then everything else // can take over (we don't have anyone to wake up). @@ -309,7 +310,7 @@ impl<T> Packet<T> { // continue to fail while active senders send data while we're dropping // data, but eventually we're guaranteed to break out of this loop // (because there is a bounded number of senders). - let mut steals = self.steals; + let mut steals = unsafe { *self.steals.get() }; while { let cnt = self.cnt.compare_and_swap( steals, DISCONNECTED, Ordering::SeqCst); @@ -332,7 +333,7 @@ impl<T> Packet<T> { // Tests to see whether this port can receive without blocking. If Ok is // returned, then that's the answer. If Err is returned, then the returned // port needs to be queried instead (an upgrade happened) - pub fn can_recv(&mut self) -> Result<bool, Receiver<T>> { + pub fn can_recv(&self) -> Result<bool, Receiver<T>> { // We peek at the queue to see if there's anything on it, and we use // this return value to determine if we should pop from the queue and // upgrade this channel immediately. If it looks like we've got an @@ -351,7 +352,7 @@ impl<T> Packet<T> { } // increment the count on the channel (used for selection) - fn bump(&mut self, amt: isize) -> isize { + fn bump(&self, amt: isize) -> isize { match self.cnt.fetch_add(amt, Ordering::SeqCst) { DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); @@ -363,7 +364,7 @@ impl<T> Packet<T> { // Attempts to start selecting on this port. Like a oneshot, this can fail // immediately because of an upgrade. - pub fn start_selection(&mut self, token: SignalToken) -> SelectionResult<T> { + pub fn start_selection(&self, token: SignalToken) -> SelectionResult<T> { match self.decrement(token) { Ok(()) => SelSuccess, Err(token) => { @@ -387,7 +388,7 @@ impl<T> Packet<T> { } // Removes a previous thread from being blocked in this port - pub fn abort_selection(&mut self, + pub fn abort_selection(&self, was_upgrade: bool) -> Result<bool, Receiver<T>> { // If we're aborting selection after upgrading from a oneshot, then // we're guarantee that no one is waiting. The only way that we could @@ -403,7 +404,7 @@ impl<T> Packet<T> { // this end. This is fine because we know it's a small bounded windows // of time until the data is actually sent. if was_upgrade { - assert_eq!(self.steals, 0); + assert_eq!(unsafe { *self.steals.get() }, 0); assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); return Ok(true) } @@ -444,8 +445,10 @@ impl<T> Packet<T> { thread::yield_now(); } } - assert_eq!(self.steals, 0); - self.steals = steals; + unsafe { + assert_eq!(*self.steals.get(), 0); + *self.steals.get() = steals; + } // if we were previously positive, then there's surely data to // receive diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index df4a3746a49..f6dbe01d7bd 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -351,6 +351,15 @@ impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("MutexGuard") + .field("lock", &self.__lock) + .finish() + } +} + pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { &guard.__lock.inner } diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 71e163321ae..a9747639aac 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -64,6 +64,7 @@ // You'll find a few more details in the implementation, but that's the gist of // it! +use fmt; use marker; use ptr; use sync::atomic::{AtomicUsize, AtomicBool, Ordering}; @@ -103,6 +104,7 @@ unsafe impl Send for Once {} /// State yielded to the `call_once_force` method which can be used to query /// whether the `Once` was previously poisoned or not. #[unstable(feature = "once_poison", issue = "33577")] +#[derive(Debug)] pub struct OnceState { poisoned: bool, } @@ -328,6 +330,13 @@ impl Once { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for Once { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Once { .. }") + } +} + impl Drop for Finish { fn drop(&mut self) { // Swap out our state with however we finished. We should only ever see diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index f83cf7ba9c2..0a11c71706b 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -362,6 +362,24 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, T: fmt::Debug> fmt::Debug for RwLockReadGuard<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RwLockReadGuard") + .field("lock", &self.__lock) + .finish() + } +} + +#[stable(feature = "std_debug", since = "1.15.0")] +impl<'a, T: fmt::Debug> fmt::Debug for RwLockWriteGuard<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RwLockWriteGuard") + .field("lock", &self.__lock) + .finish() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'rwlock, T: ?Sized> Deref for RwLockReadGuard<'rwlock, T> { type Target = T; diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs index 0c45ab8a887..14da376efa9 100644 --- a/src/libstd/sys/mod.rs +++ b/src/libstd/sys/mod.rs @@ -30,8 +30,14 @@ //! inter-dependencies within `std` that will be a challenging goal to //! achieve. +#![allow(missing_debug_implementations)] + pub use self::imp::*; +#[cfg(target_os = "redox")] +#[path = "redox/mod.rs"] +mod imp; + #[cfg(unix)] #[path = "unix/mod.rs"] mod imp; diff --git a/src/libstd/sys/redox/args.rs b/src/libstd/sys/redox/args.rs new file mode 100644 index 00000000000..f6fea2f1076 --- /dev/null +++ b/src/libstd/sys/redox/args.rs @@ -0,0 +1,109 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Global initialization and retreival of command line arguments. +//! +//! On some platforms these are stored during runtime startup, +//! and on some they are retrieved from the system on demand. + +#![allow(dead_code)] // runtime init functions not used during testing + +use ffi::OsString; +use marker::PhantomData; +use vec; + +/// One-time global initialization. +pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) } + +/// One-time global cleanup. +pub unsafe fn cleanup() { imp::cleanup() } + +/// Returns the command line arguments +pub fn args() -> Args { + imp::args() +} + +pub struct Args { + iter: vec::IntoIter<OsString>, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option<OsString> { self.iter.next() } + fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { self.iter.len() } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() } +} + +mod imp { + use os::unix::prelude::*; + use mem; + use ffi::OsString; + use marker::PhantomData; + use slice; + use str; + use super::Args; + + use sys_common::mutex::Mutex; + + static mut GLOBAL_ARGS_PTR: usize = 0; + static LOCK: Mutex = Mutex::new(); + + pub unsafe fn init(argc: isize, argv: *const *const u8) { + let mut args: Vec<Vec<u8>> = Vec::new(); + for i in 0..argc { + let len = *(argv.offset(i * 2)) as usize; + let ptr = *(argv.offset(i * 2 + 1)); + args.push(slice::from_raw_parts(ptr, len).to_vec()); + } + + LOCK.lock(); + let ptr = get_global_ptr(); + assert!((*ptr).is_none()); + (*ptr) = Some(box args); + LOCK.unlock(); + } + + pub unsafe fn cleanup() { + LOCK.lock(); + *get_global_ptr() = None; + LOCK.unlock(); + } + + pub fn args() -> Args { + let bytes = clone().unwrap_or(Vec::new()); + let v: Vec<OsString> = bytes.into_iter().map(|v| { + OsStringExt::from_vec(v) + }).collect(); + Args { iter: v.into_iter(), _dont_send_or_sync_me: PhantomData } + } + + fn clone() -> Option<Vec<Vec<u8>>> { + unsafe { + LOCK.lock(); + let ptr = get_global_ptr(); + let ret = (*ptr).as_ref().map(|s| (**s).clone()); + LOCK.unlock(); + return ret + } + } + + fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> { + unsafe { mem::transmute(&GLOBAL_ARGS_PTR) } + } + +} diff --git a/src/libstd/sys/redox/backtrace.rs b/src/libstd/sys/redox/backtrace.rs new file mode 100644 index 00000000000..6f53841502a --- /dev/null +++ b/src/libstd/sys/redox/backtrace.rs @@ -0,0 +1,18 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use libc; +use io; +use sys_common::backtrace::output; + +#[inline(never)] +pub fn write(w: &mut io::Write) -> io::Result<()> { + output(w, 0, 0 as *mut libc::c_void, None) +} diff --git a/src/libstd/sys/redox/condvar.rs b/src/libstd/sys/redox/condvar.rs new file mode 100644 index 00000000000..0ca0987b245 --- /dev/null +++ b/src/libstd/sys/redox/condvar.rs @@ -0,0 +1,106 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use intrinsics::{atomic_cxchg, atomic_xadd, atomic_xchg}; +use ptr; +use time::Duration; + +use sys::mutex::{mutex_lock, mutex_unlock, Mutex}; +use sys::syscall::{futex, FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE}; + +pub struct Condvar { + lock: UnsafeCell<*mut i32>, + seq: UnsafeCell<i32> +} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { + lock: UnsafeCell::new(ptr::null_mut()), + seq: UnsafeCell::new(0) + } + } + + #[inline] + pub unsafe fn init(&self) { + *self.lock.get() = ptr::null_mut(); + *self.seq.get() = 0; + } + + #[inline] + pub fn notify_one(&self) { + unsafe { + let seq = self.seq.get(); + + atomic_xadd(seq, 1); + + let _ = futex(seq, FUTEX_WAKE, 1, 0, ptr::null_mut()); + } + } + + #[inline] + pub fn notify_all(&self) { + unsafe { + let lock = self.lock.get(); + let seq = self.seq.get(); + + if *lock == ptr::null_mut() { + return; + } + + atomic_xadd(seq, 1); + + let _ = futex(seq, FUTEX_REQUEUE, 1, ::usize::MAX, *lock); + } + } + + #[inline] + pub fn wait(&self, mutex: &Mutex) { + unsafe { + let lock = self.lock.get(); + let seq = self.seq.get(); + + if *lock != mutex.lock.get() { + if *lock != ptr::null_mut() { + panic!("Condvar used with more than one Mutex"); + } + + atomic_cxchg(lock as *mut usize, 0, mutex.lock.get() as usize); + } + + mutex_unlock(*lock); + + let _ = futex(seq, FUTEX_WAIT, *seq, 0, ptr::null_mut()); + + while atomic_xchg(*lock, 2) != 0 { + let _ = futex(*lock, FUTEX_WAIT, 2, 0, ptr::null_mut()); + } + + mutex_lock(*lock); + } + } + + #[inline] + pub fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + ::sys_common::util::dumb_print(format_args!("condvar wait_timeout\n")); + unimplemented!(); + } + + #[inline] + pub unsafe fn destroy(&self) { + *self.lock.get() = ptr::null_mut(); + *self.seq.get() = 0; + } +} + +unsafe impl Send for Condvar {} + +unsafe impl Sync for Condvar {} diff --git a/src/libstd/sys/redox/env.rs b/src/libstd/sys/redox/env.rs new file mode 100644 index 00000000000..669b7520df8 --- /dev/null +++ b/src/libstd/sys/redox/env.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod os { + pub const FAMILY: &'static str = "redox"; + pub const OS: &'static str = "redox"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} diff --git a/src/libstd/sys/redox/ext/ffi.rs b/src/libstd/sys/redox/ext/ffi.rs new file mode 100644 index 00000000000..d59b4fc0b70 --- /dev/null +++ b/src/libstd/sys/redox/ext/ffi.rs @@ -0,0 +1,61 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extension to the primitives in the `std::ffi` module + +#![stable(feature = "rust1", since = "1.0.0")] + +use ffi::{OsStr, OsString}; +use mem; +use sys::os_str::Buf; +use sys_common::{FromInner, IntoInner, AsInner}; + +/// Unix-specific extensions to `OsString`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an `OsString` from a byte vector. + #[stable(feature = "rust1", since = "1.0.0")] + fn from_vec(vec: Vec<u8>) -> Self; + + /// Yields the underlying byte vector of this `OsString`. + #[stable(feature = "rust1", since = "1.0.0")] + fn into_vec(self) -> Vec<u8>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_vec(vec: Vec<u8>) -> OsString { + FromInner::from_inner(Buf { inner: vec }) + } + fn into_vec(self) -> Vec<u8> { + self.into_inner().inner + } +} + +/// Unix-specific extensions to `OsStr`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + #[stable(feature = "rust1", since = "1.0.0")] + fn from_bytes(slice: &[u8]) -> &Self; + + /// Gets the underlying byte view of the `OsStr` slice. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_bytes(&self) -> &[u8]; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + fn from_bytes(slice: &[u8]) -> &OsStr { + unsafe { mem::transmute(slice) } + } + fn as_bytes(&self) -> &[u8] { + &self.as_inner().inner + } +} diff --git a/src/libstd/sys/redox/ext/fs.rs b/src/libstd/sys/redox/ext/fs.rs new file mode 100644 index 00000000000..b4e220971fd --- /dev/null +++ b/src/libstd/sys/redox/ext/fs.rs @@ -0,0 +1,298 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extensions to primitives in the `std::fs` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs::{self, Permissions, OpenOptions}; +use io; +use path::Path; +use sys; +use sys_common::{FromInner, AsInner, AsInnerMut}; + +/// Unix-specific extensions to `Permissions` +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait PermissionsExt { + /// Returns the underlying raw `mode_t` bits that are the standard Unix + /// permissions for this file. + /// + /// # Examples + /// + /// ```rust,ignore + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// let f = try!(File::create("foo.txt")); + /// let metadata = try!(f.metadata()); + /// let permissions = metadata.permissions(); + /// + /// println!("permissions: {}", permissions.mode()); + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn mode(&self) -> u32; + + /// Sets the underlying raw bits for this set of permissions. + /// + /// # Examples + /// + /// ```rust,ignore + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// let f = try!(File::create("foo.txt")); + /// let metadata = try!(f.metadata()); + /// let mut permissions = metadata.permissions(); + /// + /// permissions.set_mode(0o644); // Read/write for owner and read for others. + /// assert_eq!(permissions.mode(), 0o644); + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn set_mode(&mut self, mode: u32); + + /// Creates a new instance of `Permissions` from the given set of Unix + /// permission bits. + /// + /// # Examples + /// + /// ```rust,ignore + /// use std::fs::Permissions; + /// use std::os::unix::fs::PermissionsExt; + /// + /// // Read/write for owner and read for others. + /// let permissions = Permissions::from_mode(0o644); + /// assert_eq!(permissions.mode(), 0o644); + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn from_mode(mode: u32) -> Self; +} + +#[stable(feature = "fs_ext", since = "1.1.0")] +impl PermissionsExt for Permissions { + fn mode(&self) -> u32 { + self.as_inner().mode() + } + + fn set_mode(&mut self, mode: u32) { + *self = Permissions::from_inner(FromInner::from_inner(mode)); + } + + fn from_mode(mode: u32) -> Permissions { + Permissions::from_inner(FromInner::from_inner(mode)) + } +} + +/// Unix-specific extensions to `OpenOptions` +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of a `File::open_opts` call then this + /// specified `mode` will be used as the permission bits for the new file. + /// If no `mode` is set, the default of `0o666` will be used. + /// The operating system masks out bits with the systems `umask`, to produce + /// the final permissions. + /// + /// # Examples + /// + /// ```rust,ignore + /// extern crate libc; + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// let mut options = OpenOptions::new(); + /// options.mode(0o644); // Give read/write for owner and read for others. + /// let file = options.open("foo.txt"); + /// ``` + #[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`. + /// + /// 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. + /// + /// Custom flags can only set flags, not remove flags set by Rusts options. + /// This options overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```rust,ignore + /// extern crate libc; + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// let mut options = OpenOptions::new(); + /// options.write(true); + /// if cfg!(unix) { + /// options.custom_flags(libc::O_NOFOLLOW); + /// } + /// let file = options.open("foo.txt"); + /// ``` + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn custom_flags(&mut self, flags: i32) -> &mut Self; +} + +#[stable(feature = "fs_ext", since = "1.1.0")] +impl OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: u32) -> &mut OpenOptions { + self.as_inner_mut().mode(mode); self + } + + fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { + self.as_inner_mut().custom_flags(flags); self + } +} + +// Hm, why are there casts here to the returned type, shouldn't the types always +// be the same? Right you are! Turns out, however, on android at least the types +// in the raw `stat` structure are not the same as the types being returned. Who +// knew! +// +// As a result to make sure this compiles for all platforms we do the manual +// casts and rely on manual lowering to `stat` if the raw type is desired. +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mode(&self) -> u32; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn uid(&self) -> u32; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn gid(&self) -> u32; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn size(&self) -> u64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn atime(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mtime(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ctime(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ctime_nsec(&self) -> i64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for fs::Metadata { + fn mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } +} + +/// Add special unix types (block/char device, fifo and socket) +#[stable(feature = "file_type_ext", since = "1.5.0")] +pub trait FileTypeExt { + /// Returns whether this file type is a block device. + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_block_device(&self) -> bool; + /// Returns whether this file type is a char device. + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_char_device(&self) -> bool; + /// Returns whether this file type is a fifo. + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_fifo(&self) -> bool; + /// Returns whether this file type is a socket. + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_socket(&self) -> bool; +} + +#[stable(feature = "file_type_ext", since = "1.5.0")] +impl FileTypeExt for fs::FileType { + fn is_block_device(&self) -> bool { false /*FIXME: Implement block device mode*/ } + fn is_char_device(&self) -> bool { false /*FIXME: Implement char device mode*/ } + fn is_fifo(&self) -> bool { false /*FIXME: Implement fifo mode*/ } + fn is_socket(&self) -> bool { false /*FIXME: Implement socket mode*/ } +} + +/// Creates a new symbolic link on the filesystem. +/// +/// The `dst` path will be a symbolic link pointing to the `src` path. +/// +/// # Note +/// +/// On Windows, you must specify whether a symbolic link points to a file +/// or directory. Use `os::windows::fs::symlink_file` to create a +/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a +/// symbolic link to a directory. Additionally, the process must have +/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a +/// symbolic link. +/// +/// # Examples +/// +/// ``` +/// use std::os::unix::fs; +/// +/// # fn foo() -> std::io::Result<()> { +/// try!(fs::symlink("a.txt", "b.txt")); +/// # Ok(()) +/// # } +/// ``` +#[stable(feature = "symlink", since = "1.1.0")] +pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> +{ + sys::fs::symlink(src.as_ref(), dst.as_ref()) +} + +#[stable(feature = "dir_builder", since = "1.6.0")] +/// An extension trait for `fs::DirBuilder` for unix-specific options. +pub trait DirBuilderExt { + /// Sets the mode to create new directories with. This option defaults to + /// 0o777. + /// + /// # Examples + /// + /// ```ignore + /// use std::fs::DirBuilder; + /// use std::os::unix::fs::DirBuilderExt; + /// + /// let mut builder = DirBuilder::new(); + /// builder.mode(0o755); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + fn mode(&mut self, mode: u32) -> &mut Self; +} + +#[stable(feature = "dir_builder", since = "1.6.0")] +impl DirBuilderExt for fs::DirBuilder { + fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder { + self.as_inner_mut().set_mode(mode); + self + } +} diff --git a/src/libstd/sys/redox/ext/io.rs b/src/libstd/sys/redox/ext/io.rs new file mode 100644 index 00000000000..8e7cc593dbd --- /dev/null +++ b/src/libstd/sys/redox/ext/io.rs @@ -0,0 +1,151 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extensions to general I/O primitives + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs; +use net; +use sys; +use sys_common::{self, AsInner, FromInner, IntoInner}; + +/// Raw file descriptors. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawFd = usize; + +/// A trait to extract the raw unix file descriptor from an underlying +/// object. +/// +/// This is only available on unix platforms and must be imported in order +/// to call the method. Windows platforms have a corresponding `AsRawHandle` +/// and `AsRawSocket` set of traits. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawFd { + /// Extracts the raw file descriptor. + /// + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guaranteed to be valid while + /// the original object has not yet been destroyed. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_fd(&self) -> RawFd; +} + +/// A trait to express the ability to construct an object from a raw file +/// descriptor. +#[stable(feature = "from_raw_os", since = "1.1.0")] +pub trait FromRawFd { + /// Constructs a new instances of `Self` from the given raw file + /// descriptor. + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + #[stable(feature = "from_raw_os", since = "1.1.0")] + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw file descriptor. +#[stable(feature = "into_raw_os", since = "1.4.0")] +pub trait IntoRawFd { + /// Consumes this object, returning the raw underlying file descriptor. + /// + /// This function **transfers ownership** of the underlying file descriptor + /// to the caller. Callers are then the unique owners of the file descriptor + /// and must close the descriptor once it's no longer needed. + #[stable(feature = "into_raw_os", since = "1.4.0")] + fn into_raw_fd(self) -> RawFd; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> fs::File { + fs::File::from_inner(sys::fs::File::from_inner(fd)) + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for fs::File { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_inner().fd().raw() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_inner().fd().raw() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_inner().fd().raw() + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { + let file = sys::fs::File::from_inner(fd); + net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(file)) + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { + let file = sys::fs::File::from_inner(fd); + net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(file)) + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { + let file = sys::fs::File::from_inner(fd); + net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(file)) + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::TcpStream { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_inner().into_fd().into_raw() + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::TcpListener { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_inner().into_fd().into_raw() + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::UdpSocket { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_inner().into_fd().into_raw() + } +} diff --git a/src/libstd/sys/redox/ext/mod.rs b/src/libstd/sys/redox/ext/mod.rs new file mode 100644 index 00000000000..513ef272e97 --- /dev/null +++ b/src/libstd/sys/redox/ext/mod.rs @@ -0,0 +1,50 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Experimental extensions to `std` for Unix platforms. +//! +//! For now, this module is limited to extracting file descriptors, +//! but its functionality will grow over time. +//! +//! # Example +//! +//! ```no_run +//! use std::fs::File; +//! use std::os::unix::prelude::*; +//! +//! fn main() { +//! let f = File::create("foo.txt").unwrap(); +//! let fd = f.as_raw_fd(); +//! +//! // use fd with native unix bindings +//! } +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +pub mod ffi; +pub mod fs; +pub mod io; +pub mod process; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::fs::{FileTypeExt, PermissionsExt, OpenOptionsExt, MetadataExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::process::{CommandExt, ExitStatusExt}; +} diff --git a/src/libstd/sys/redox/ext/process.rs b/src/libstd/sys/redox/ext/process.rs new file mode 100644 index 00000000000..c59524974bf --- /dev/null +++ b/src/libstd/sys/redox/ext/process.rs @@ -0,0 +1,183 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extensions to primitives in the `std::process` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use io; +use os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd}; +use process; +use sys; +use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner}; + +/// Unix-specific extensions to the `std::process::Command` builder +#[stable(feature = "rust1", since = "1.0.0")] +pub trait CommandExt { + /// Sets the child process's user id. This translates to a + /// `setuid` call in the child process. Failure in the `setuid` + /// call will cause the spawn to fail. + #[stable(feature = "rust1", since = "1.0.0")] + fn uid(&mut self, id: u32) -> &mut process::Command; + + /// Similar to `uid`, but sets the group id of the child process. This has + /// the same semantics as the `uid` field. + #[stable(feature = "rust1", since = "1.0.0")] + fn gid(&mut self, id: u32) -> &mut process::Command; + + /// Schedules a closure to be run just before the `exec` function is + /// invoked. + /// + /// The closure is allowed to return an I/O error whose OS error code will + /// be communicated back to the parent and returned as an error from when + /// the spawn was requested. + /// + /// Multiple closures can be registered and they will be called in order of + /// their registration. If a closure returns `Err` then no further closures + /// will be called and the spawn operation will immediately return with a + /// failure. + /// + /// # 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 + /// 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 + /// other threads perhaps still running when the `fork` was run). + /// + /// When this closure is run, aspects such as the stdio file descriptors and + /// working directory have successfully been changed, so output to these + /// locations may not appear where intended. + #[stable(feature = "process_exec", since = "1.15.0")] + fn before_exec<F>(&mut self, f: F) -> &mut process::Command + where F: FnMut() -> io::Result<()> + Send + Sync + 'static; + + /// Performs all the required setup by this `Command`, followed by calling + /// the `execvp` syscall. + /// + /// On success this function will not return, and otherwise it will return + /// an error indicating why the exec (or another part of the setup of the + /// `Command`) failed. + /// + /// This function, unlike `spawn`, will **not** `fork` the process to create + /// a new child. Like spawn, however, the default behavior for the stdio + /// descriptors will be to inherited from the current process. + /// + /// # Notes + /// + /// The process may be in a "broken state" if this function returns in + /// error. For example the working directory, environment variables, signal + /// handling settings, various user/group information, or aspects of stdio + /// file descriptors may have changed. If a "transactional spawn" is + /// required to gracefully handle errors it is recommended to use the + /// cross-platform `spawn` instead. + #[stable(feature = "process_exec2", since = "1.9.0")] + fn exec(&mut self) -> io::Error; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl CommandExt for process::Command { + fn uid(&mut self, id: u32) -> &mut process::Command { + self.as_inner_mut().uid(id); + self + } + + fn gid(&mut self, id: u32) -> &mut process::Command { + self.as_inner_mut().gid(id); + self + } + + fn before_exec<F>(&mut self, f: F) -> &mut process::Command + where F: FnMut() -> io::Result<()> + Send + Sync + 'static + { + self.as_inner_mut().before_exec(Box::new(f)); + self + } + + fn exec(&mut self) -> io::Error { + self.as_inner_mut().exec(sys::process::Stdio::Inherit) + } +} + +/// Unix-specific extensions to `std::process::ExitStatus` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait ExitStatusExt { + /// Creates a new `ExitStatus` from the raw underlying `i32` return value of + /// a process. + #[stable(feature = "exit_status_from", since = "1.12.0")] + fn from_raw(raw: i32) -> Self; + + /// If the process was terminated by a signal, returns that signal. + #[stable(feature = "rust1", since = "1.0.0")] + fn signal(&self) -> Option<i32>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExitStatusExt for process::ExitStatus { + fn from_raw(raw: i32) -> Self { + process::ExitStatus::from_inner(From::from(raw)) + } + + fn signal(&self) -> Option<i32> { + self.as_inner().signal() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl FromRawFd for process::Stdio { + unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { + let fd = sys::fd::FileDesc::new(fd); + let io = sys::process::Stdio::Fd(fd); + process::Stdio::from_inner(io) + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdin { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdout { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStderr { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdin { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdout { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStderr { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} diff --git a/src/libstd/sys/redox/fast_thread_local.rs b/src/libstd/sys/redox/fast_thread_local.rs new file mode 100644 index 00000000000..6eeae2d90ea --- /dev/null +++ b/src/libstd/sys/redox/fast_thread_local.rs @@ -0,0 +1,116 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(target_thread_local)] +#![unstable(feature = "thread_local_internals", issue = "0")] + +use cell::{Cell, UnsafeCell}; +use intrinsics; +use ptr; + +pub struct Key<T> { + inner: UnsafeCell<Option<T>>, + + // Metadata to keep track of the state of the destructor. Remember that + // these variables are thread-local, not global. + dtor_registered: Cell<bool>, + dtor_running: Cell<bool>, +} + +unsafe impl<T> ::marker::Sync for Key<T> { } + +impl<T> Key<T> { + pub const fn new() -> Key<T> { + Key { + inner: UnsafeCell::new(None), + dtor_registered: Cell::new(false), + dtor_running: Cell::new(false) + } + } + + pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { + unsafe { + if intrinsics::needs_drop::<T>() && self.dtor_running.get() { + return None + } + self.register_dtor(); + } + Some(&self.inner) + } + + unsafe fn register_dtor(&self) { + if !intrinsics::needs_drop::<T>() || self.dtor_registered.get() { + return + } + + register_dtor(self as *const _ as *mut u8, + destroy_value::<T>); + self.dtor_registered.set(true); + } +} + +unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { + // The fallback implementation uses a vanilla OS-based TLS key to track + // the list of destructors that need to be run for this thread. The key + // then has its own destructor which runs all the other destructors. + // + // The destructor for DTORS is a little special in that it has a `while` + // loop to continuously drain the list of registered destructors. It + // *should* be the case that this loop always terminates because we + // provide the guarantee that a TLS key cannot be set after it is + // flagged for destruction. + use sys_common::thread_local as os; + + static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors)); + type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; + if DTORS.get().is_null() { + let v: Box<List> = box Vec::new(); + DTORS.set(Box::into_raw(v) as *mut u8); + } + let list: &mut List = &mut *(DTORS.get() as *mut List); + list.push((t, dtor)); + + unsafe extern fn run_dtors(mut ptr: *mut u8) { + while !ptr.is_null() { + let list: Box<List> = Box::from_raw(ptr as *mut List); + for &(ptr, dtor) in list.iter() { + dtor(ptr); + } + ptr = DTORS.get(); + DTORS.set(ptr::null_mut()); + } + } +} + +pub unsafe extern fn destroy_value<T>(ptr: *mut u8) { + let ptr = ptr as *mut Key<T>; + // Right before we run the user destructor be sure to flag the + // destructor as running for this thread so calls to `get` will return + // `None`. + (*ptr).dtor_running.set(true); + + // The OSX implementation of TLS apparently had an odd aspect to it + // where the pointer we have may be overwritten while this destructor + // is running. Specifically if a TLS destructor re-accesses TLS it may + // trigger a re-initialization of all TLS variables, paving over at + // least some destroyed ones with initial values. + // + // This means that if we drop a TLS value in place on OSX that we could + // revert the value to its original state halfway through the + // destructor, which would be bad! + // + // Hence, we use `ptr::read` on OSX (to move to a "safe" location) + // instead of drop_in_place. + if cfg!(target_os = "macos") { + ptr::read((*ptr).inner.get()); + } else { + ptr::drop_in_place((*ptr).inner.get()); + } +} diff --git a/src/libstd/sys/redox/fd.rs b/src/libstd/sys/redox/fd.rs new file mode 100644 index 00000000000..b6de68a9dc1 --- /dev/null +++ b/src/libstd/sys/redox/fd.rs @@ -0,0 +1,100 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(reason = "not public", issue = "0", feature = "fd")] + +use io::{self, Read}; +use mem; +use sys::{cvt, syscall}; +use sys_common::AsInner; +use sys_common::io::read_to_end_uninitialized; + +pub struct FileDesc { + fd: usize, +} + +impl FileDesc { + pub fn new(fd: usize) -> FileDesc { + FileDesc { fd: fd } + } + + pub fn raw(&self) -> usize { self.fd } + + /// Extracts the actual filedescriptor without closing it. + pub fn into_raw(self) -> usize { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + cvt(syscall::read(self.fd, buf)) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + cvt(syscall::write(self.fd, buf)) + } + + pub fn duplicate(&self) -> io::Result<FileDesc> { + let new_fd = cvt(syscall::dup(self.fd, &[]))?; + Ok(FileDesc::new(new_fd)) + } + + pub fn nonblocking(&self) -> io::Result<bool> { + let flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?; + Ok(flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK) + } + + pub fn set_cloexec(&self) -> io::Result<()> { + let mut flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?; + flags |= syscall::O_CLOEXEC; + cvt(syscall::fcntl(self.fd, syscall::F_SETFL, flags)).and(Ok(())) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?; + if nonblocking { + flags |= syscall::O_NONBLOCK; + } else { + flags &= !syscall::O_NONBLOCK; + } + cvt(syscall::fcntl(self.fd, syscall::F_SETFL, flags)).and(Ok(())) + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (**self).read(buf) + } + + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + unsafe { read_to_end_uninitialized(self, buf) } + } +} + +impl AsInner<usize> for FileDesc { + fn as_inner(&self) -> &usize { &self.fd } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // (opened after we closed ours. + let _ = syscall::close(self.fd); + } +} diff --git a/src/libstd/sys/redox/fs.rs b/src/libstd/sys/redox/fs.rs new file mode 100644 index 00000000000..a8391d2b898 --- /dev/null +++ b/src/libstd/sys/redox/fs.rs @@ -0,0 +1,470 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use os::unix::prelude::*; + +use ffi::{OsString, OsStr}; +use fmt; +use io::{self, Error, ErrorKind, SeekFrom}; +use path::{Path, PathBuf}; +use sync::Arc; +use sys::fd::FileDesc; +use sys::time::SystemTime; +use sys::{cvt, syscall}; +use sys_common::{AsInner, FromInner}; + +pub struct File(FileDesc); + +#[derive(Clone)] +pub struct FileAttr { + stat: syscall::Stat, +} + +pub struct ReadDir { + data: Vec<u8>, + i: usize, + root: Arc<PathBuf>, +} + +struct Dir(FileDesc); + +unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} + +pub struct DirEntry { + root: Arc<PathBuf>, + name: Box<[u8]> +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + // generic + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, + // system-specific + custom_flags: i32, + mode: u16, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { mode: u16 } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType { mode: u16 } + +#[derive(Debug)] +pub struct DirBuilder { mode: u16 } + +impl FileAttr { + pub fn size(&self) -> u64 { self.stat.st_size as u64 } + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat.st_mode as u16) & 0o777 } + } + + pub fn file_type(&self) -> FileType { + FileType { mode: self.stat.st_mode as u16 } + } +} + +impl FileAttr { + pub fn modified(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(syscall::TimeSpec { + tv_sec: self.stat.st_mtime as i64, + tv_nsec: self.stat.st_mtime_nsec as i32, + })) + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(syscall::TimeSpec { + tv_sec: self.stat.st_atime as i64, + tv_nsec: self.stat.st_atime_nsec as i32, + })) + } + + pub fn created(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(syscall::TimeSpec { + tv_sec: self.stat.st_ctime as i64, + tv_nsec: self.stat.st_ctime_nsec as i32, + })) + } +} + +impl AsInner<syscall::Stat> for FileAttr { + fn as_inner(&self) -> &syscall::Stat { &self.stat } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.mode &= !0o222; + } else { + self.mode |= 0o222; + } + } + pub fn mode(&self) -> u32 { self.mode as u32 } +} + +impl FileType { + pub fn is_dir(&self) -> bool { self.is(syscall::MODE_DIR) } + pub fn is_file(&self) -> bool { self.is(syscall::MODE_FILE) } + pub fn is_symlink(&self) -> bool { false /*FIXME: Implement symlink mode*/ } + + pub fn is(&self, mode: u16) -> bool { + self.mode & (syscall::MODE_DIR | syscall::MODE_FILE) == mode + } +} + +impl FromInner<u32> for FilePermissions { + fn from_inner(mode: u32) -> FilePermissions { + FilePermissions { mode: mode as u16 } + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("/home")' + fmt::Debug::fmt(&*self.root, f) + } +} + +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; + + fn next(&mut self) -> Option<io::Result<DirEntry>> { + loop { + let start = self.i; + let mut i = self.i; + while i < self.data.len() { + self.i += 1; + if self.data[i] == b'\n' { + break; + } + i += 1; + } + if start < self.i { + let ret = DirEntry { + name: self.data[start .. i].to_owned().into_boxed_slice(), + root: self.root.clone() + }; + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)) + } + } else { + return None; + } + } + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.root.join(OsStr::from_bytes(self.name_bytes())) + } + + pub fn file_name(&self) -> OsString { + OsStr::from_bytes(self.name_bytes()).to_os_string() + } + + pub fn metadata(&self) -> io::Result<FileAttr> { + lstat(&self.path()) + } + + pub fn file_type(&self) -> io::Result<FileType> { + lstat(&self.path()).map(|m| m.file_type()) + } + + fn name_bytes(&self) -> &[u8] { + &*self.name + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { + // generic + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + // system-specific + custom_flags: 0, + mode: 0o666, + } + } + + pub fn read(&mut self, read: bool) { self.read = read; } + pub fn write(&mut self, write: bool) { self.write = write; } + pub fn append(&mut self, append: bool) { self.append = append; } + pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; } + pub fn create(&mut self, create: bool) { self.create = create; } + pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; } + + pub fn custom_flags(&mut self, flags: i32) { self.custom_flags = flags; } + pub fn mode(&mut self, mode: u32) { self.mode = mode as u16; } + + fn get_access_mode(&self) -> io::Result<usize> { + match (self.read, self.write, self.append) { + (true, false, false) => Ok(syscall::O_RDONLY), + (false, true, false) => Ok(syscall::O_WRONLY), + (true, true, false) => Ok(syscall::O_RDWR), + (false, _, true) => Ok(syscall::O_WRONLY | syscall::O_APPEND), + (true, _, true) => Ok(syscall::O_RDWR | syscall::O_APPEND), + (false, false, false) => Err(Error::from_raw_os_error(syscall::EINVAL)), + } + } + + fn get_creation_mode(&self) -> io::Result<usize> { + match (self.write, self.append) { + (true, false) => {} + (false, false) => + if self.truncate || self.create || self.create_new { + return Err(Error::from_raw_os_error(syscall::EINVAL)); + }, + (_, true) => + if self.truncate && !self.create_new { + return Err(Error::from_raw_os_error(syscall::EINVAL)); + }, + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => 0, + (true, false, false) => syscall::O_CREAT, + (false, true, false) => syscall::O_TRUNC, + (true, true, false) => syscall::O_CREAT | syscall::O_TRUNC, + (_, _, true) => syscall::O_CREAT | syscall::O_EXCL, + }) + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { + let flags = syscall::O_CLOEXEC | + opts.get_access_mode()? as usize | + opts.get_creation_mode()? as usize | + (opts.custom_flags as usize & !syscall::O_ACCMODE); + let fd = cvt(syscall::open(path.to_str().unwrap(), flags | opts.mode as usize))?; + Ok(File(FileDesc::new(fd))) + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + let mut stat = syscall::Stat::default(); + cvt(syscall::fstat(self.0.raw(), &mut stat))?; + Ok(FileAttr { stat: stat }) + } + + pub fn fsync(&self) -> io::Result<()> { + cvt(syscall::fsync(self.0.raw()))?; + Ok(()) + } + + pub fn datasync(&self) -> io::Result<()> { + self.fsync() + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + cvt(syscall::ftruncate(self.0.raw(), size as usize))?; + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn flush(&self) -> io::Result<()> { Ok(()) } + + pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { + let (whence, pos) = match pos { + // Casting to `i64` is fine, too large values will end up as + // negative which will cause an error in `lseek64`. + SeekFrom::Start(off) => (syscall::SEEK_SET, off as i64), + SeekFrom::End(off) => (syscall::SEEK_END, off), + SeekFrom::Current(off) => (syscall::SEEK_CUR, off), + }; + let n = cvt(syscall::lseek(self.0.raw(), pos as isize, whence))?; + Ok(n as u64) + } + + pub fn duplicate(&self) -> io::Result<File> { + self.0.duplicate().map(File) + } + + pub fn dup(&self, buf: &[u8]) -> io::Result<File> { + let fd = cvt(syscall::dup(*self.fd().as_inner() as usize, buf))?; + Ok(File(FileDesc::new(fd))) + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + set_perm(&self.path()?, perm) + } + + pub fn path(&self) -> io::Result<PathBuf> { + let mut buf: [u8; 4096] = [0; 4096]; + let count = cvt(syscall::fpath(*self.fd().as_inner() as usize, &mut buf))?; + Ok(PathBuf::from(unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) })) + } + + pub fn fd(&self) -> &FileDesc { &self.0 } + + pub fn into_fd(self) -> FileDesc { self.0 } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let flags = syscall::O_CREAT | syscall::O_CLOEXEC | syscall::O_DIRECTORY | syscall::O_EXCL; + let fd = cvt(syscall::open(p.to_str().unwrap(), flags | (self.mode as usize & 0o777)))?; + let _ = syscall::close(fd); + Ok(()) + } + + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as u16; + } +} + +impl FromInner<usize> for File { + fn from_inner(fd: usize) -> File { + File(FileDesc::new(fd)) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut b = f.debug_struct("File"); + b.field("fd", &self.0.raw()); + if let Ok(path) = self.path() { + b.field("path", &path); + } + /* + if let Some((read, write)) = get_mode(fd) { + b.field("read", &read).field("write", &write); + } + */ + b.finish() + } +} + +pub fn readdir(p: &Path) -> io::Result<ReadDir> { + let root = Arc::new(p.to_path_buf()); + + let flags = syscall::O_CLOEXEC | syscall::O_RDONLY | syscall::O_DIRECTORY; + let fd = cvt(syscall::open(p.to_str().unwrap(), flags))?; + let file = FileDesc::new(fd); + let mut data = Vec::new(); + file.read_to_end(&mut data)?; + + Ok(ReadDir { data: data, i: 0, root: root }) +} + +pub fn unlink(p: &Path) -> io::Result<()> { + cvt(syscall::unlink(p.to_str().unwrap()))?; + Ok(()) +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + ::sys_common::util::dumb_print(format_args!("Rename\n")); + unimplemented!(); +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + cvt(syscall::chmod(p.to_str().unwrap(), perm.mode as usize))?; + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + cvt(syscall::rmdir(p.to_str().unwrap()))?; + Ok(()) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let filetype = lstat(path)?.file_type(); + if filetype.is_symlink() { + unlink(path) + } else { + remove_dir_all_recursive(path) + } +} + +fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { + for child in readdir(path)? { + let child = child?; + if child.file_type()?.is_dir() { + remove_dir_all_recursive(&child.path())?; + } else { + unlink(&child.path())?; + } + } + rmdir(path) +} + +pub fn readlink(p: &Path) -> io::Result<PathBuf> { + canonicalize(p) +} + +pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> { + ::sys_common::util::dumb_print(format_args!("Symlink\n")); + unimplemented!(); +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + ::sys_common::util::dumb_print(format_args!("Link\n")); + unimplemented!(); +} + +pub fn stat(p: &Path) -> io::Result<FileAttr> { + let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_CLOEXEC | syscall::O_STAT))?; + let file = File(FileDesc::new(fd)); + file.file_attr() +} + +pub fn lstat(p: &Path) -> io::Result<FileAttr> { + stat(p) +} + +pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { + let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_CLOEXEC | syscall::O_STAT))?; + let file = File(FileDesc::new(fd)); + file.path() +} + +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + use fs::{File, set_permissions}; + if !from.is_file() { + return Err(Error::new(ErrorKind::InvalidInput, + "the source path is not an existing regular file")) + } + + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + let perm = reader.metadata()?.permissions(); + + let ret = io::copy(&mut reader, &mut writer)?; + set_permissions(to, perm)?; + Ok(ret) +} diff --git a/src/test/compile-fail/issue-23305.rs b/src/libstd/sys/redox/memchr.rs index 4acb1f70d34..4c314b7a472 100644 --- a/src/test/compile-fail/issue-23305.rs +++ b/src/libstd/sys/redox/memchr.rs @@ -7,12 +7,8 @@ // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. +// +// Original implementation taken from rust-memchr +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch -pub trait ToNbt<T> { - fn new(val: T) -> Self; -} - -impl ToNbt<Self> {} //~ ERROR use of `Self` outside of an impl or trait -//~^ ERROR the trait `ToNbt` cannot be made into an object - -fn main() {} +pub use sys_common::memchr::fallback::{memchr, memrchr}; diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs new file mode 100644 index 00000000000..5982bdd6549 --- /dev/null +++ b/src/libstd/sys/redox/mod.rs @@ -0,0 +1,95 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code, missing_docs, bad_style)] + +use io::{self, ErrorKind}; + +pub mod args; +#[cfg(any(not(cargobuild), feature = "backtrace"))] +pub mod backtrace; +pub mod condvar; +pub mod env; +pub mod ext; +pub mod fast_thread_local; +pub mod fd; +pub mod fs; +pub mod memchr; +pub mod mutex; +pub mod net; +pub mod os; +pub mod os_str; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rand; +pub mod rwlock; +pub mod stack_overflow; +pub mod stdio; +pub mod syscall; +pub mod thread; +pub mod thread_local; +pub mod time; + +#[cfg(not(test))] +pub fn init() { + use alloc::oom; + + oom::set_oom_handler(oom_handler); + + // A nicer handler for out-of-memory situations than the default one. This + // one prints a message to stderr before aborting. It is critical that this + // code does not allocate any memory since we are in an OOM situation. Any + // errors are ignored while printing since there's nothing we can do about + // them and we are about to exit anyways. + fn oom_handler() -> ! { + use intrinsics; + let msg = "fatal runtime error: out of memory\n"; + unsafe { + let _ = syscall::write(2, msg.as_bytes()); + intrinsics::abort(); + } + } +} + +pub fn decode_error_kind(errno: i32) -> ErrorKind { + match errno { + syscall::ECONNREFUSED => ErrorKind::ConnectionRefused, + syscall::ECONNRESET => ErrorKind::ConnectionReset, + syscall::EPERM | syscall::EACCES => ErrorKind::PermissionDenied, + syscall::EPIPE => ErrorKind::BrokenPipe, + syscall::ENOTCONN => ErrorKind::NotConnected, + syscall::ECONNABORTED => ErrorKind::ConnectionAborted, + syscall::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + syscall::EADDRINUSE => ErrorKind::AddrInUse, + syscall::ENOENT => ErrorKind::NotFound, + syscall::EINTR => ErrorKind::Interrupted, + syscall::EINVAL => ErrorKind::InvalidInput, + syscall::ETIMEDOUT => ErrorKind::TimedOut, + syscall::EEXIST => ErrorKind::AlreadyExists, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == syscall::EAGAIN || x == syscall::EWOULDBLOCK => + ErrorKind::WouldBlock, + + _ => ErrorKind::Other, + } +} + +pub fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> { + result.map_err(|err| io::Error::from_raw_os_error(err.errno)) +} + +/// On Redox, use an illegal instruction to abort +pub unsafe fn abort_internal() -> ! { + ::core::intrinsics::abort(); +} diff --git a/src/libstd/sys/redox/mutex.rs b/src/libstd/sys/redox/mutex.rs new file mode 100644 index 00000000000..a995f597fc4 --- /dev/null +++ b/src/libstd/sys/redox/mutex.rs @@ -0,0 +1,179 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use intrinsics::{atomic_cxchg, atomic_xchg}; +use ptr; + +use sys::syscall::{futex, getpid, FUTEX_WAIT, FUTEX_WAKE}; + +pub unsafe fn mutex_try_lock(m: *mut i32) -> bool { + atomic_cxchg(m, 0, 1).0 == 0 +} + +pub unsafe fn mutex_lock(m: *mut i32) { + let mut c = 0; + //Set to larger value for longer spin test + for _i in 0..100 { + c = atomic_cxchg(m, 0, 1).0; + if c == 0 { + break; + } + //cpu_relax() + } + if c == 1 { + c = atomic_xchg(m, 2); + } + while c != 0 { + let _ = futex(m, FUTEX_WAIT, 2, 0, ptr::null_mut()); + c = atomic_xchg(m, 2); + } +} + +pub unsafe fn mutex_unlock(m: *mut i32) { + if *m == 2 { + *m = 0; + } else if atomic_xchg(m, 0) == 1 { + return; + } + //Set to larger value for longer spin test + for _i in 0..100 { + if *m != 0 { + if atomic_cxchg(m, 1, 2).0 != 0 { + return; + } + } + //cpu_relax() + } + let _ = futex(m, FUTEX_WAKE, 1, 0, ptr::null_mut()); +} + +pub struct Mutex { + pub lock: UnsafeCell<i32>, +} + +impl Mutex { + /// Create a new mutex. + pub const fn new() -> Self { + Mutex { + lock: UnsafeCell::new(0), + } + } + + #[inline] + pub unsafe fn init(&self) { + *self.lock.get() = 0; + } + + /// Try to lock the mutex + #[inline] + pub unsafe fn try_lock(&self) -> bool { + mutex_try_lock(self.lock.get()) + } + + /// Lock the mutex + #[inline] + pub unsafe fn lock(&self) { + mutex_lock(self.lock.get()); + } + + /// Unlock the mutex + #[inline] + pub unsafe fn unlock(&self) { + mutex_unlock(self.lock.get()); + } + + #[inline] + pub unsafe fn destroy(&self) { + *self.lock.get() = 0; + } +} + +unsafe impl Send for Mutex {} + +unsafe impl Sync for Mutex {} + +pub struct ReentrantMutex { + pub lock: UnsafeCell<i32>, + pub owner: UnsafeCell<usize>, + pub own_count: UnsafeCell<usize>, +} + +impl ReentrantMutex { + pub const fn uninitialized() -> Self { + ReentrantMutex { + lock: UnsafeCell::new(0), + owner: UnsafeCell::new(0), + own_count: UnsafeCell::new(0), + } + } + + #[inline] + pub unsafe fn init(&mut self) { + *self.lock.get() = 0; + *self.owner.get() = 0; + *self.own_count.get() = 0; + } + + /// Try to lock the mutex + #[inline] + pub unsafe fn try_lock(&self) -> bool { + let pid = getpid().unwrap(); + if *self.own_count.get() > 0 && *self.owner.get() == pid { + *self.own_count.get() += 1; + true + } else { + if mutex_try_lock(self.lock.get()) { + *self.owner.get() = pid; + *self.own_count.get() = 1; + true + } else { + false + } + } + } + + /// Lock the mutex + #[inline] + pub unsafe fn lock(&self) { + let pid = getpid().unwrap(); + if *self.own_count.get() > 0 && *self.owner.get() == pid { + *self.own_count.get() += 1; + } else { + mutex_lock(self.lock.get()); + *self.owner.get() = pid; + *self.own_count.get() = 1; + } + } + + /// Unlock the mutex + #[inline] + pub unsafe fn unlock(&self) { + let pid = getpid().unwrap(); + if *self.own_count.get() > 0 && *self.owner.get() == pid { + *self.own_count.get() -= 1; + if *self.own_count.get() == 0 { + *self.owner.get() = 0; + mutex_unlock(self.lock.get()); + } + } + } + + #[inline] + pub unsafe fn destroy(&self) { + *self.lock.get() = 0; + *self.owner.get() = 0; + *self.own_count.get() = 0; + } +} + +unsafe impl Send for ReentrantMutex {} + +unsafe impl Sync for ReentrantMutex {} diff --git a/src/libstd/sys/redox/net/dns/answer.rs b/src/libstd/sys/redox/net/dns/answer.rs new file mode 100644 index 00000000000..8e6aaeb0293 --- /dev/null +++ b/src/libstd/sys/redox/net/dns/answer.rs @@ -0,0 +1,22 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use string::String; +use vec::Vec; + +#[derive(Clone, Debug)] +pub struct DnsAnswer { + pub name: String, + pub a_type: u16, + pub a_class: u16, + pub ttl_a: u16, + pub ttl_b: u16, + pub data: Vec<u8> +} diff --git a/src/libstd/sys/redox/net/dns/mod.rs b/src/libstd/sys/redox/net/dns/mod.rs new file mode 100644 index 00000000000..43c4fe7ac9d --- /dev/null +++ b/src/libstd/sys/redox/net/dns/mod.rs @@ -0,0 +1,217 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::answer::DnsAnswer; +pub use self::query::DnsQuery; + +use slice; +use u16; +use string::String; +use vec::Vec; + +mod answer; +mod query; + +#[unstable(feature = "n16", issue="0")] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct n16 { + inner: u16 +} + +impl n16 { + #[unstable(feature = "n16", issue="0")] + pub fn as_bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts((&self.inner as *const u16) as *const u8, 2) } + } + + #[unstable(feature = "n16", issue="0")] + pub fn from_bytes(bytes: &[u8]) -> Self { + n16 { + inner: unsafe { slice::from_raw_parts(bytes.as_ptr() as *const u16, bytes.len()/2)[0] } + } + } +} + +#[unstable(feature = "n16", issue="0")] +impl From<u16> for n16 { + fn from(value: u16) -> Self { + n16 { + inner: value.to_be() + } + } +} + +#[unstable(feature = "n16", issue="0")] +impl From<n16> for u16 { + fn from(value: n16) -> Self { + u16::from_be(value.inner) + } +} + +#[derive(Clone, Debug)] +pub struct Dns { + pub transaction_id: u16, + pub flags: u16, + pub queries: Vec<DnsQuery>, + pub answers: Vec<DnsAnswer> +} + +impl Dns { + pub fn compile(&self) -> Vec<u8> { + let mut data = Vec::new(); + + macro_rules! push_u8 { + ($value:expr) => { + data.push($value); + }; + }; + + macro_rules! push_n16 { + ($value:expr) => { + data.extend_from_slice(n16::from($value).as_bytes()); + }; + }; + + push_n16!(self.transaction_id); + push_n16!(self.flags); + push_n16!(self.queries.len() as u16); + push_n16!(self.answers.len() as u16); + push_n16!(0); + push_n16!(0); + + for query in self.queries.iter() { + for part in query.name.split('.') { + push_u8!(part.len() as u8); + data.extend_from_slice(part.as_bytes()); + } + push_u8!(0); + push_n16!(query.q_type); + push_n16!(query.q_class); + } + + data + } + + pub fn parse(data: &[u8]) -> Result<Self, String> { + let mut i = 0; + + macro_rules! pop_u8 { + () => { + { + i += 1; + if i > data.len() { + return Err(format!("{}: {}: pop_u8", file!(), line!())); + } + data[i - 1] + } + }; + }; + + macro_rules! pop_n16 { + () => { + { + i += 2; + if i > data.len() { + return Err(format!("{}: {}: pop_n16", file!(), line!())); + } + u16::from(n16::from_bytes(&data[i - 2 .. i])) + } + }; + }; + + macro_rules! pop_data { + () => { + { + let mut data = Vec::new(); + + let data_len = pop_n16!(); + for _data_i in 0..data_len { + data.push(pop_u8!()); + } + + data + } + }; + }; + + macro_rules! pop_name { + () => { + { + let mut name = String::new(); + + loop { + let name_len = pop_u8!(); + if name_len == 0 { + break; + } + if ! name.is_empty() { + name.push('.'); + } + for _name_i in 0..name_len { + name.push(pop_u8!() as char); + } + } + + name + } + }; + }; + + let transaction_id = pop_n16!(); + let flags = pop_n16!(); + let queries_len = pop_n16!(); + let answers_len = pop_n16!(); + pop_n16!(); + pop_n16!(); + + let mut queries = Vec::new(); + for _query_i in 0..queries_len { + queries.push(DnsQuery { + name: pop_name!(), + q_type: pop_n16!(), + q_class: pop_n16!() + }); + } + + 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!() + }, + a_type: pop_n16!(), + a_class: pop_n16!(), + ttl_a: pop_n16!(), + ttl_b: pop_n16!(), + data: pop_data!() + }); + } + + Ok(Dns { + transaction_id: transaction_id, + flags: flags, + queries: queries, + answers: answers, + }) + } +} diff --git a/src/libstd/sys/redox/net/dns/query.rs b/src/libstd/sys/redox/net/dns/query.rs new file mode 100644 index 00000000000..b0dcdcb624a --- /dev/null +++ b/src/libstd/sys/redox/net/dns/query.rs @@ -0,0 +1,18 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use string::String; + +#[derive(Clone, Debug)] +pub struct DnsQuery { + pub name: String, + pub q_type: u16, + pub q_class: u16 +} diff --git a/src/libstd/sys/redox/net/mod.rs b/src/libstd/sys/redox/net/mod.rs new file mode 100644 index 00000000000..3fdf61cfed8 --- /dev/null +++ b/src/libstd/sys/redox/net/mod.rs @@ -0,0 +1,113 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fs::File; +use io::{Error, Result, Read}; +use iter::Iterator; +use net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use str::FromStr; +use string::{String, ToString}; +use sys::syscall::EINVAL; +use time; +use vec::{IntoIter, Vec}; + +use self::dns::{Dns, DnsQuery}; + +pub use self::tcp::{TcpStream, TcpListener}; +pub use self::udp::UdpSocket; + +pub mod netc; + +mod dns; +mod tcp; +mod udp; + +pub struct LookupHost(IntoIter<SocketAddr>); + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } +} + +pub fn lookup_host(host: &str) -> Result<LookupHost> { + let mut ip_string = String::new(); + File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?; + let ip: Vec<u8> = ip_string.trim().split(".").map(|part| part.parse::<u8>() + .unwrap_or(0)).collect(); + + let mut dns_string = String::new(); + File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?; + let dns: Vec<u8> = dns_string.trim().split(".").map(|part| part.parse::<u8>() + .unwrap_or(0)).collect(); + + if ip.len() == 4 && dns.len() == 4 { + let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap(); + let tid = (time.subsec_nanos() >> 16) as u16; + + let packet = Dns { + transaction_id: tid, + flags: 0x0100, + queries: vec![DnsQuery { + name: host.to_string(), + q_type: 0x0001, + q_class: 0x0001, + }], + answers: vec![] + }; + + let packet_data = packet.compile(); + + let my_ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]); + let dns_ip = Ipv4Addr::new(dns[0], dns[1], dns[2], dns[3]); + let socket = UdpSocket::bind(&SocketAddr::V4(SocketAddrV4::new(my_ip, 0)))?; + socket.connect(&SocketAddr::V4(SocketAddrV4::new(dns_ip, 53)))?; + socket.send(&packet_data)?; + + let mut buf = [0; 65536]; + let count = socket.recv(&mut buf)?; + + match Dns::parse(&buf[.. count]) { + Ok(response) => { + let mut addrs = vec![]; + for answer in response.answers.iter() { + if answer.a_type == 0x0001 && answer.a_class == 0x0001 + && answer.data.len() == 4 + { + let answer_ip = Ipv4Addr::new(answer.data[0], + answer.data[1], + answer.data[2], + answer.data[3]); + addrs.push(SocketAddr::V4(SocketAddrV4::new(answer_ip, 0))); + } + } + Ok(LookupHost(addrs.into_iter())) + }, + Err(_err) => Err(Error::from_raw_os_error(EINVAL)) + } + } else { + Err(Error::from_raw_os_error(EINVAL)) + } +} + +fn path_to_peer_addr(path_str: &str) -> SocketAddr { + let mut parts = path_str.split('/').next().unwrap_or("").split(':').skip(1); + let host = Ipv4Addr::from_str(parts.next().unwrap_or("")).unwrap_or(Ipv4Addr::new(0, 0, 0, 0)); + let port = parts.next().unwrap_or("").parse::<u16>().unwrap_or(0); + SocketAddr::V4(SocketAddrV4::new(host, port)) +} + +fn path_to_local_addr(path_str: &str) -> SocketAddr { + let mut parts = path_str.split('/').nth(1).unwrap_or("").split(':'); + let host = Ipv4Addr::from_str(parts.next().unwrap_or("")).unwrap_or(Ipv4Addr::new(0, 0, 0, 0)); + let port = parts.next().unwrap_or("").parse::<u16>().unwrap_or(0); + SocketAddr::V4(SocketAddrV4::new(host, port)) +} diff --git a/src/libstd/sys/redox/net/netc.rs b/src/libstd/sys/redox/net/netc.rs new file mode 100644 index 00000000000..03e1c9fffa4 --- /dev/null +++ b/src/libstd/sys/redox/net/netc.rs @@ -0,0 +1,57 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub type in_addr_t = u32; +pub type in_port_t = u16; + +pub type socklen_t = u32; +pub type sa_family_t = u16; + +pub const AF_INET: sa_family_t = 1; +pub const AF_INET6: sa_family_t = 2; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct in_addr { + pub s_addr: in_addr_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct in6_addr { + pub s6_addr: [u8; 16], + __align: [u32; 0], +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct sockaddr { + pub sa_family: sa_family_t, + pub sa_data: [u8; 14], +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: in_port_t, + pub sin_addr: in_addr, + pub sin_zero: [u8; 8], +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: in_port_t, + pub sin6_flowinfo: u32, + pub sin6_addr: in6_addr, + pub sin6_scope_id: u32, +} diff --git a/src/libstd/sys/redox/net/tcp.rs b/src/libstd/sys/redox/net/tcp.rs new file mode 100644 index 00000000000..d5362c9f131 --- /dev/null +++ b/src/libstd/sys/redox/net/tcp.rs @@ -0,0 +1,199 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io::{Error, ErrorKind, Result}; +use net::{SocketAddr, Shutdown}; +use path::Path; +use sys::fs::{File, OpenOptions}; +use sys_common::{AsInner, FromInner, IntoInner}; +use time::Duration; +use vec::Vec; + +use super::{path_to_peer_addr, path_to_local_addr}; + +#[derive(Debug)] +pub struct TcpStream(File); + +impl TcpStream { + pub fn connect(addr: &SocketAddr) -> Result<TcpStream> { + let path = format!("tcp:{}", addr); + let mut options = OpenOptions::new(); + options.read(true); + options.write(true); + Ok(TcpStream(File::open(&Path::new(path.as_str()), &options)?)) + } + + pub fn duplicate(&self) -> Result<TcpStream> { + Ok(TcpStream(self.0.dup(&[])?)) + } + + pub fn read(&self, buf: &mut [u8]) -> Result<usize> { + self.0.read(buf) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> { + self.0.read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> Result<usize> { + self.0.write(buf) + } + + pub fn take_error(&self) -> Result<Option<Error>> { + Ok(None) + } + + pub fn peer_addr(&self) -> Result<SocketAddr> { + let path = self.0.path()?; + Ok(path_to_peer_addr(path.to_str().unwrap_or(""))) + } + + pub fn socket_addr(&self) -> Result<SocketAddr> { + let path = self.0.path()?; + Ok(path_to_local_addr(path.to_str().unwrap_or(""))) + } + + pub fn shutdown(&self, _how: Shutdown) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::shutdown not implemented")) + } + + pub fn nodelay(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "TcpStream::nodelay not implemented")) + } + + pub fn nonblocking(&self) -> Result<bool> { + self.0.fd().nonblocking() + } + + pub fn only_v6(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "TcpStream::only_v6 not implemented")) + } + + pub fn ttl(&self) -> Result<u32> { + Err(Error::new(ErrorKind::Other, "TcpStream::ttl not implemented")) + } + + pub fn read_timeout(&self) -> Result<Option<Duration>> { + Err(Error::new(ErrorKind::Other, "TcpStream::read_timeout not implemented")) + } + + pub fn write_timeout(&self) -> Result<Option<Duration>> { + Err(Error::new(ErrorKind::Other, "TcpStream::write_timeout not implemented")) + } + + pub fn set_nodelay(&self, _nodelay: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_nodelay not implemented")) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { + self.0.fd().set_nonblocking(nonblocking) + } + + pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_only_v6 not implemented")) + } + + pub fn set_ttl(&self, _ttl: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_ttl not implemented")) + } + + pub fn set_read_timeout(&self, _dur: Option<Duration>) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_read_timeout not implemented")) + } + + pub fn set_write_timeout(&self, _dur: Option<Duration>) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_write_timeout not implemented")) + } +} + +impl AsInner<File> for TcpStream { + fn as_inner(&self) -> &File { &self.0 } +} + +impl FromInner<File> for TcpStream { + fn from_inner(file: File) -> TcpStream { + TcpStream(file) + } +} + +impl IntoInner<File> for TcpStream { + fn into_inner(self) -> File { self.0 } +} + +#[derive(Debug)] +pub struct TcpListener(File); + +impl TcpListener { + pub fn bind(addr: &SocketAddr) -> Result<TcpListener> { + let path = format!("tcp:/{}", addr); + let mut options = OpenOptions::new(); + options.read(true); + options.write(true); + Ok(TcpListener(File::open(&Path::new(path.as_str()), &options)?)) + } + + pub fn accept(&self) -> Result<(TcpStream, SocketAddr)> { + let file = self.0.dup(b"listen")?; + let path = file.path()?; + let peer_addr = path_to_peer_addr(path.to_str().unwrap_or("")); + Ok((TcpStream(file), peer_addr)) + } + + pub fn duplicate(&self) -> Result<TcpListener> { + Ok(TcpListener(self.0.dup(&[])?)) + } + + pub fn take_error(&self) -> Result<Option<Error>> { + Ok(None) + } + + pub fn socket_addr(&self) -> Result<SocketAddr> { + let path = self.0.path()?; + Ok(path_to_local_addr(path.to_str().unwrap_or(""))) + } + + pub fn nonblocking(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "TcpListener::nonblocking not implemented")) + } + + pub fn only_v6(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "TcpListener::only_v6 not implemented")) + } + + pub fn ttl(&self) -> Result<u32> { + Err(Error::new(ErrorKind::Other, "TcpListener::ttl not implemented")) + } + + pub fn set_nonblocking(&self, _nonblocking: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpListener::set_nonblocking not implemented")) + } + + pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpListener::set_only_v6 not implemented")) + } + + pub fn set_ttl(&self, _ttl: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpListener::set_ttl not implemented")) + } +} + +impl AsInner<File> for TcpListener { + fn as_inner(&self) -> &File { &self.0 } +} + +impl FromInner<File> for TcpListener { + fn from_inner(file: File) -> TcpListener { + TcpListener(file) + } +} + +impl IntoInner<File> for TcpListener { + fn into_inner(self) -> File { self.0 } +} diff --git a/src/libstd/sys/redox/net/udp.rs b/src/libstd/sys/redox/net/udp.rs new file mode 100644 index 00000000000..607c66c2ba7 --- /dev/null +++ b/src/libstd/sys/redox/net/udp.rs @@ -0,0 +1,188 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use io::{Error, ErrorKind, Result}; +use net::{SocketAddr, Ipv4Addr, Ipv6Addr}; +use path::Path; +use sys::fs::{File, OpenOptions}; +use sys_common::{AsInner, FromInner, IntoInner}; +use time::Duration; + +use super::{path_to_peer_addr, path_to_local_addr}; + +#[derive(Debug)] +pub struct UdpSocket(File, UnsafeCell<Option<SocketAddr>>); + +impl UdpSocket { + pub fn bind(addr: &SocketAddr) -> Result<UdpSocket> { + let path = format!("udp:/{}", addr); + let mut options = OpenOptions::new(); + options.read(true); + options.write(true); + Ok(UdpSocket(File::open(&Path::new(path.as_str()), &options)?, UnsafeCell::new(None))) + } + + fn get_conn(&self) -> &mut Option<SocketAddr> { + unsafe { &mut *(self.1.get()) } + } + + pub fn connect(&self, addr: &SocketAddr) -> Result<()> { + unsafe { *self.1.get() = Some(*addr) }; + Ok(()) + } + + pub fn duplicate(&self) -> Result<UdpSocket> { + let new_bind = self.0.dup(&[])?; + let new_conn = *self.get_conn(); + Ok(UdpSocket(new_bind, UnsafeCell::new(new_conn))) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> { + let from = self.0.dup(b"listen")?; + let path = from.path()?; + let peer_addr = path_to_peer_addr(path.to_str().unwrap_or("")); + let count = from.read(buf)?; + Ok((count, peer_addr)) + } + + pub fn recv(&self, buf: &mut [u8]) -> Result<usize> { + if let Some(addr) = *self.get_conn() { + let from = self.0.dup(format!("{}", addr).as_bytes())?; + from.read(buf) + } else { + Err(Error::new(ErrorKind::Other, "UdpSocket::recv not connected")) + } + } + + pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> Result<usize> { + let to = self.0.dup(format!("{}", addr).as_bytes())?; + to.write(buf) + } + + pub fn send(&self, buf: &[u8]) -> Result<usize> { + if let Some(addr) = *self.get_conn() { + self.send_to(buf, &addr) + } else { + Err(Error::new(ErrorKind::Other, "UdpSocket::send not connected")) + } + } + + pub fn take_error(&self) -> Result<Option<Error>> { + Ok(None) + } + + pub fn socket_addr(&self) -> Result<SocketAddr> { + let path = self.0.path()?; + Ok(path_to_local_addr(path.to_str().unwrap_or(""))) + } + + pub fn broadcast(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "UdpSocket::broadcast not implemented")) + } + + pub fn multicast_loop_v4(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_loop_v4 not implemented")) + } + + pub fn multicast_loop_v6(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_loop_v6 not implemented")) + } + + pub fn multicast_ttl_v4(&self) -> Result<u32> { + Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_ttl_v4 not implemented")) + } + + pub fn nonblocking(&self) -> Result<bool> { + self.0.fd().nonblocking() + } + + pub fn only_v6(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "UdpSocket::only_v6 not implemented")) + } + + pub fn ttl(&self) -> Result<u32> { + Err(Error::new(ErrorKind::Other, "UdpSocket::ttl not implemented")) + } + + pub fn read_timeout(&self) -> Result<Option<Duration>> { + Err(Error::new(ErrorKind::Other, "UdpSocket::read_timeout not implemented")) + } + + pub fn write_timeout(&self) -> Result<Option<Duration>> { + Err(Error::new(ErrorKind::Other, "UdpSocket::write_timeout not implemented")) + } + + pub fn set_broadcast(&self, _broadcast: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_broadcast not implemented")) + } + + pub fn set_multicast_loop_v4(&self, _multicast_loop_v4: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_loop_v4 not implemented")) + } + + pub fn set_multicast_loop_v6(&self, _multicast_loop_v6: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_loop_v6 not implemented")) + } + + pub fn set_multicast_ttl_v4(&self, _multicast_ttl_v4: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_ttl_v4 not implemented")) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { + self.0.fd().set_nonblocking(nonblocking) + } + + pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_only_v6 not implemented")) + } + + pub fn set_ttl(&self, _ttl: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_ttl not implemented")) + } + + pub fn set_read_timeout(&self, _dur: Option<Duration>) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_read_timeout not implemented")) + } + + pub fn set_write_timeout(&self, _dur: Option<Duration>) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_write_timeout not implemented")) + } + + pub fn join_multicast_v4(&self, _multiaddr: &Ipv4Addr, _interface: &Ipv4Addr) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::join_multicast_v4 not implemented")) + } + + pub fn join_multicast_v6(&self, _multiaddr: &Ipv6Addr, _interface: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::join_multicast_v6 not implemented")) + } + + pub fn leave_multicast_v4(&self, _multiaddr: &Ipv4Addr, _interface: &Ipv4Addr) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::leave_multicast_v4 not implemented")) + } + + pub fn leave_multicast_v6(&self, _multiaddr: &Ipv6Addr, _interface: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::leave_multicast_v6 not implemented")) + } +} + +impl AsInner<File> for UdpSocket { + fn as_inner(&self) -> &File { &self.0 } +} + +impl FromInner<File> for UdpSocket { + fn from_inner(file: File) -> UdpSocket { + UdpSocket(file, UnsafeCell::new(None)) + } +} + +impl IntoInner<File> for UdpSocket { + fn into_inner(self) -> File { self.0 } +} diff --git a/src/libstd/sys/redox/os.rs b/src/libstd/sys/redox/os.rs new file mode 100644 index 00000000000..135e972bca4 --- /dev/null +++ b/src/libstd/sys/redox/os.rs @@ -0,0 +1,204 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of `std::os` functionality for unix systems + +#![allow(unused_imports)] // lots of cfg code here + +use os::unix::prelude::*; + +use error::Error as StdError; +use ffi::{OsString, OsStr}; +use fmt; +use io::{self, Read, Write}; +use iter; +use marker::PhantomData; +use mem; +use memchr; +use path::{self, PathBuf}; +use ptr; +use slice; +use str; +use sys_common::mutex::Mutex; +use sys::{cvt, fd, syscall}; +use vec; + +const TMPBUF_SZ: usize = 128; +static ENV_LOCK: Mutex = Mutex::new(); + +/// Returns the platform-specific value of errno +pub fn errno() -> i32 { + 0 +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(errno: i32) -> String { + if let Some(string) = syscall::STR_ERROR.get(errno as usize) { + string.to_string() + } else { + "unknown error".to_string() + } +} + +pub fn getcwd() -> io::Result<PathBuf> { + let mut buf = [0; 4096]; + let count = cvt(syscall::getcwd(&mut buf))?; + Ok(PathBuf::from(OsString::from_vec(buf[.. count].to_vec()))) +} + +pub fn chdir(p: &path::Path) -> io::Result<()> { + cvt(syscall::chdir(p.to_str().unwrap())).and(Ok(())) +} + +pub struct SplitPaths<'a> { + iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>, + fn(&'a [u8]) -> PathBuf>, +} + +pub fn split_paths(unparsed: &OsStr) -> SplitPaths { + fn bytes_to_path(b: &[u8]) -> PathBuf { + PathBuf::from(<OsStr as OsStrExt>::from_bytes(b)) + } + fn is_colon(b: &u8) -> bool { *b == b':' } + let unparsed = unparsed.as_bytes(); + SplitPaths { + iter: unparsed.split(is_colon as fn(&u8) -> bool) + .map(bytes_to_path as fn(&[u8]) -> PathBuf) + } +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option<PathBuf> { self.iter.next() } + fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> + where I: Iterator<Item=T>, T: AsRef<OsStr> +{ + let mut joined = Vec::new(); + let sep = b':'; + + for (i, path) in paths.enumerate() { + let path = path.as_ref().as_bytes(); + if i > 0 { joined.push(sep) } + if path.contains(&sep) { + return Err(JoinPathsError) + } + joined.extend_from_slice(path); + } + Ok(OsStringExt::from_vec(joined)) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "path segment contains separator `:`".fmt(f) + } +} + +impl StdError for JoinPathsError { + fn description(&self) -> &str { "failed to join paths" } +} + +pub fn current_exe() -> io::Result<PathBuf> { + use fs::File; + + let mut file = File::open("sys:exe")?; + + let mut path = String::new(); + file.read_to_string(&mut path)?; + + if path.ends_with('\n') { + path.pop(); + } + + Ok(PathBuf::from(path)) +} + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() } + fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + let mut variables: Vec<(OsString, OsString)> = Vec::new(); + if let Ok(mut file) = ::fs::File::open("env:") { + let mut string = String::new(); + if file.read_to_string(&mut string).is_ok() { + for line in string.lines() { + let mut parts = line.splitn(2, '='); + if let Some(name) = parts.next() { + let value = parts.next().unwrap_or(""); + variables.push((OsString::from(name.to_string()), + OsString::from(value.to_string()))); + } + } + } + } + Env { iter: variables.into_iter(), _dont_send_or_sync_me: PhantomData } +} + +pub fn getenv(key: &OsStr) -> io::Result<Option<OsString>> { + if ! key.is_empty() { + if let Ok(mut file) = ::fs::File::open(&("env:".to_owned() + key.to_str().unwrap())) { + let mut string = String::new(); + file.read_to_string(&mut string)?; + Ok(Some(OsString::from(string))) + } else { + Ok(None) + } + } else { + Ok(None) + } +} + +pub fn setenv(key: &OsStr, value: &OsStr) -> io::Result<()> { + if ! key.is_empty() { + let mut file = ::fs::File::open(&("env:".to_owned() + key.to_str().unwrap()))?; + file.write_all(value.as_bytes())?; + file.set_len(value.len() as u64)?; + } + Ok(()) +} + +pub fn unsetenv(key: &OsStr) -> io::Result<()> { + ::fs::remove_file(&("env:".to_owned() + key.to_str().unwrap()))?; + Ok(()) +} + +pub fn page_size() -> usize { + 4096 +} + +pub fn temp_dir() -> PathBuf { + ::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| { + PathBuf::from("/tmp") + }) +} + +pub fn home_dir() -> Option<PathBuf> { + return ::env::var_os("HOME").map(PathBuf::from); +} + +pub fn exit(code: i32) -> ! { + let _ = syscall::exit(code as usize); + unreachable!(); +} diff --git a/src/libstd/sys/redox/os_str.rs b/src/libstd/sys/redox/os_str.rs new file mode 100644 index 00000000000..8922bf04f56 --- /dev/null +++ b/src/libstd/sys/redox/os_str.rs @@ -0,0 +1,119 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Unix systems: just +/// a `Vec<u8>`/`[u8]`. + +use borrow::Cow; +use fmt::{self, Debug}; +use str; +use mem; +use sys_common::{AsInner, IntoInner}; + +#[derive(Clone, Hash)] +pub struct Buf { + pub inner: Vec<u8> +} + +pub struct Slice { + pub inner: [u8] +} + +impl Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.to_string_lossy().fmt(formatter) + } +} + +impl Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.as_slice().fmt(formatter) + } +} + +impl IntoInner<Vec<u8>> for Buf { + fn into_inner(self) -> Vec<u8> { + self.inner + } +} + +impl AsInner<[u8]> for Buf { + fn as_inner(&self) -> &[u8] { + &self.inner + } +} + + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: s.into_bytes() } + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Buf { + Buf { + inner: Vec::with_capacity(capacity) + } + } + + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } + + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(&*self.inner) } + } + + pub fn into_string(self) -> Result<String, Buf> { + String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.extend_from_slice(&s.inner) + } +} + +impl Slice { + fn from_u8_slice(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + pub fn from_str(s: &str) -> &Slice { + Slice::from_u8_slice(s.as_bytes()) + } + + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(&self.inner).ok() + } + + pub fn to_string_lossy(&self) -> Cow<str> { + String::from_utf8_lossy(&self.inner) + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_vec() } + } +} diff --git a/src/libstd/sys/redox/path.rs b/src/libstd/sys/redox/path.rs new file mode 100644 index 00000000000..e6a267dd5d9 --- /dev/null +++ b/src/libstd/sys/redox/path.rs @@ -0,0 +1,39 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsStr; +use path::Prefix; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' +} + +pub fn parse_prefix(path: &OsStr) -> Option<Prefix> { + if let Some(path_str) = path.to_str() { + if let Some(_i) = path_str.find(':') { + // FIXME: Redox specific prefix + // Some(Prefix::Verbatim(OsStr::new(&path_str[..i]))) + None + } else { + None + } + } else { + None + } +} + +pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/redox/pipe.rs b/src/libstd/sys/redox/pipe.rs new file mode 100644 index 00000000000..e7240fbe7bf --- /dev/null +++ b/src/libstd/sys/redox/pipe.rs @@ -0,0 +1,107 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::{cvt, syscall}; +use sys::fd::FileDesc; + +//////////////////////////////////////////////////////////////////////////////// +// Anonymous pipes +//////////////////////////////////////////////////////////////////////////////// + +pub struct AnonPipe(FileDesc); + +pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut fds = [0; 2]; + cvt(syscall::pipe2(&mut fds, syscall::O_CLOEXEC))?; + Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))) +} + +impl AnonPipe { + pub fn from_fd(fd: FileDesc) -> io::Result<AnonPipe> { + fd.set_cloexec()?; + Ok(AnonPipe(fd)) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn fd(&self) -> &FileDesc { &self.0 } + pub fn into_fd(self) -> FileDesc { self.0 } +} + +pub fn read2(p1: AnonPipe, + v1: &mut Vec<u8>, + p2: AnonPipe, + v2: &mut Vec<u8>) -> io::Result<()> { + //FIXME: Use event based I/O multiplexing + //unimplemented!() + + p1.read_to_end(v1)?; + p2.read_to_end(v2)?; + + Ok(()) + + /* + // Set both pipes into nonblocking mode as we're gonna be reading from both + // in the `select` loop below, and we wouldn't want one to block the other! + let p1 = p1.into_fd(); + let p2 = p2.into_fd(); + p1.set_nonblocking(true)?; + p2.set_nonblocking(true)?; + + loop { + // wait for either pipe to become readable using `select` + cvt_r(|| unsafe { + let mut read: libc::fd_set = mem::zeroed(); + libc::FD_SET(p1.raw(), &mut read); + libc::FD_SET(p2.raw(), &mut read); + libc::select(max + 1, &mut read, ptr::null_mut(), ptr::null_mut(), + ptr::null_mut()) + })?; + + // Read as much as we can from each pipe, ignoring EWOULDBLOCK or + // EAGAIN. If we hit EOF, then this will happen because the underlying + // reader will return Ok(0), in which case we'll see `Ok` ourselves. In + // this case we flip the other fd back into blocking mode and read + // whatever's leftover on that file descriptor. + let read = |fd: &FileDesc, dst: &mut Vec<u8>| { + match fd.read_to_end(dst) { + Ok(_) => Ok(true), + Err(e) => { + if e.raw_os_error() == Some(libc::EWOULDBLOCK) || + e.raw_os_error() == Some(libc::EAGAIN) { + Ok(false) + } else { + Err(e) + } + } + } + }; + if read(&p1, v1)? { + p2.set_nonblocking(false)?; + return p2.read_to_end(v2).map(|_| ()); + } + if read(&p2, v2)? { + p1.set_nonblocking(false)?; + return p1.read_to_end(v1).map(|_| ()); + } + } + */ +} diff --git a/src/libstd/sys/redox/process.rs b/src/libstd/sys/redox/process.rs new file mode 100644 index 00000000000..849f51013e6 --- /dev/null +++ b/src/libstd/sys/redox/process.rs @@ -0,0 +1,504 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use collections::hash_map::HashMap; +use env; +use ffi::OsStr; +use fmt; +use io::{self, Error, ErrorKind}; +use path::Path; +use sys::fd::FileDesc; +use sys::fs::{File, OpenOptions}; +use sys::pipe::{self, AnonPipe}; +use sys::{cvt, syscall}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + // Currently we try hard to ensure that the call to `.exec()` doesn't + // actually allocate any memory. While many platforms try to ensure that + // memory allocation works after a fork in a multithreaded process, it's + // been observed to be buggy and somewhat unreliable, so we do our best to + // just not do it at all! + // + // Along those lines, the `argv` and `envp` raw pointers here are exactly + // what's gonna get passed to `execvp`. The `argv` array starts with the + // `program` and ends with a NULL, and the `envp` pointer, if present, is + // also null-terminated. + // + // Right now we don't support removing arguments, so there's no much fancy + // support there, but we support adding and removing environment variables, + // so a side table is used to track where in the `envp` array each key is + // located. Whenever we add a key we update it in place if it's already + // present, and whenever we remove a key we update the locations of all + // other keys. + program: String, + args: Vec<String>, + env: HashMap<String, String>, + + cwd: Option<String>, + uid: Option<u32>, + gid: Option<u32>, + saw_nul: bool, + closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>, + stdin: Option<Stdio>, + stdout: Option<Stdio>, + stderr: Option<Stdio>, +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option<AnonPipe>, + pub stdout: Option<AnonPipe>, + pub stderr: Option<AnonPipe>, +} + +// passed to do_exec() with configuration of what the child stdio should look +// like +struct ChildPipes { + stdin: ChildStdio, + stdout: ChildStdio, + stderr: ChildStdio, +} + +enum ChildStdio { + Inherit, + Explicit(usize), + Owned(FileDesc), +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(FileDesc), +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_str().unwrap().to_owned(), + args: Vec::new(), + env: HashMap::new(), + cwd: None, + uid: None, + gid: None, + saw_nul: false, + closures: Vec::new(), + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_str().unwrap().to_owned()); + } + + pub fn env(&mut self, key: &OsStr, val: &OsStr) { + self.env.insert(key.to_str().unwrap().to_owned(), val.to_str().unwrap().to_owned()); + } + + pub fn env_remove(&mut self, key: &OsStr) { + self.env.remove(key.to_str().unwrap()); + } + + pub fn env_clear(&mut self) { + self.env.clear(); + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_str().unwrap().to_owned()); + } + pub fn uid(&mut self, id: u32) { + self.uid = Some(id); + } + pub fn gid(&mut self, id: u32) { + self.gid = Some(id); + } + + pub fn before_exec(&mut self, + f: Box<FnMut() -> io::Result<()> + Send + Sync>) { + self.closures.push(f); + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + + if self.saw_nul { + return Err(io::Error::new(ErrorKind::InvalidInput, + "nul byte found in provided data")); + } + + let (ours, theirs) = self.setup_io(default, needs_stdin)?; + let (input, output) = pipe::anon_pipe()?; + + let pid = unsafe { + match cvt(syscall::clone(0))? { + 0 => { + drop(input); + let err = self.do_exec(theirs); + let errno = err.raw_os_error().unwrap_or(syscall::EINVAL) as u32; + let bytes = [ + (errno >> 24) as u8, + (errno >> 16) as u8, + (errno >> 8) as u8, + (errno >> 0) as u8, + CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] + ]; + // pipe I/O up to PIPE_BUF bytes should be atomic, and then + // we want to be sure we *don't* run at_exit destructors as + // we're being torn down regardless + assert!(output.write(&bytes).is_ok()); + let _ = syscall::exit(1); + panic!("failed to exit"); + } + n => n, + } + }; + + let mut p = Process { pid: pid, status: None }; + drop(output); + let mut bytes = [0; 8]; + + // loop to handle EINTR + loop { + match input.read(&mut bytes) { + Ok(0) => return Ok((p, ours)), + Ok(8) => { + assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), + "Validation on the CLOEXEC pipe failed: {:?}", bytes); + let errno = combine(&bytes[0.. 4]); + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + return Err(Error::from_raw_os_error(errno)) + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => { + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("the CLOEXEC pipe failed: {:?}", e) + }, + Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } + } + } + + fn combine(arr: &[u8]) -> i32 { + let a = arr[0] as u32; + let b = arr[1] as u32; + let c = arr[2] as u32; + let d = arr[3] as u32; + + ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 + } + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + if self.saw_nul { + return io::Error::new(ErrorKind::InvalidInput, + "nul byte found in provided data") + } + + match self.setup_io(default, true) { + Ok((_, theirs)) => unsafe { self.do_exec(theirs) }, + Err(e) => e, + } + } + + // And at this point we've reached a special time in the life of the + // child. The child must now be considered hamstrung and unable to + // do anything other than syscalls really. Consider the following + // scenario: + // + // 1. Thread A of process 1 grabs the malloc() mutex + // 2. Thread B of process 1 forks(), creating thread C + // 3. Thread C of process 2 then attempts to malloc() + // 4. The memory of process 2 is the same as the memory of + // process 1, so the mutex is locked. + // + // This situation looks a lot like deadlock, right? It turns out + // that this is what pthread_atfork() takes care of, which is + // presumably implemented across platforms. The first thing that + // threads to *before* forking is to do things like grab the malloc + // mutex, and then after the fork they unlock it. + // + // Despite this information, libnative's spawn has been witnessed to + // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but + // all collected backtraces point at malloc/free traffic in the + // child spawned process. + // + // For this reason, the block of code below should contain 0 + // invocations of either malloc of free (or their related friends). + // + // As an example of not having malloc/free traffic, we don't close + // this file descriptor by dropping the FileDesc (which contains an + // allocation). Instead we just close it manually. This will never + // have the drop glue anyway because this code never returns (the + // child will either exec() or invoke syscall::exit) + unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error { + macro_rules! t { + ($e:expr) => (match $e { + Ok(e) => e, + Err(e) => return e, + }) + } + + if let Some(fd) = stdio.stderr.fd() { + let _ = syscall::close(2); + t!(cvt(syscall::dup(fd, &[]))); + let _ = syscall::close(fd); + } + if let Some(fd) = stdio.stdout.fd() { + let _ = syscall::close(1); + t!(cvt(syscall::dup(fd, &[]))); + let _ = syscall::close(fd); + } + if let Some(fd) = stdio.stdin.fd() { + let _ = syscall::close(0); + t!(cvt(syscall::dup(fd, &[]))); + let _ = syscall::close(fd); + } + + if let Some(g) = self.gid { + t!(cvt(syscall::setregid(g as usize, g as usize))); + } + if let Some(u) = self.uid { + t!(cvt(syscall::setreuid(u as usize, u as usize))); + } + if let Some(ref cwd) = self.cwd { + t!(cvt(syscall::chdir(cwd))); + } + + for callback in self.closures.iter_mut() { + t!(callback()); + } + + let mut args: Vec<[usize; 2]> = Vec::new(); + args.push([self.program.as_ptr() as usize, self.program.len()]); + for arg in self.args.iter() { + args.push([arg.as_ptr() as usize, arg.len()]); + } + + for (key, val) in self.env.iter() { + env::set_var(key, val); + } + + let program = if self.program.contains(':') || self.program.contains('/') { + self.program.to_owned() + } else { + let mut path_env = ::env::var("PATH").unwrap_or(".".to_string()); + + if ! path_env.ends_with('/') { + path_env.push('/'); + } + + path_env.push_str(&self.program); + + path_env + }; + + if let Err(err) = syscall::execve(&program, &args) { + io::Error::from_raw_os_error(err.errno as i32) + } else { + panic!("return from exec without err"); + } + } + + + fn setup_io(&self, default: Stdio, needs_stdin: bool) + -> io::Result<(StdioPipes, ChildPipes)> { + let null = Stdio::Null; + let default_stdin = if needs_stdin {&default} else {&null}; + let stdin = self.stdin.as_ref().unwrap_or(default_stdin); + let stdout = self.stdout.as_ref().unwrap_or(&default); + let stderr = self.stderr.as_ref().unwrap_or(&default); + let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; + let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; + let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; + let ours = StdioPipes { + stdin: our_stdin, + stdout: our_stdout, + stderr: our_stderr, + }; + let theirs = ChildPipes { + stdin: their_stdin, + stdout: their_stdout, + stderr: their_stderr, + }; + Ok((ours, theirs)) + } +} + +impl Stdio { + fn to_child_stdio(&self, readable: bool) + -> io::Result<(ChildStdio, Option<AnonPipe>)> { + match *self { + Stdio::Inherit => Ok((ChildStdio::Inherit, None)), + + // Make sure that the source descriptors are not an stdio + // descriptor, otherwise the order which we set the child's + // descriptors may blow away a descriptor which we are hoping to + // save. For example, suppose we want the child's stderr to be the + // parent's stdout, and the child's stdout to be the parent's + // stderr. No matter which we dup first, the second will get + // overwritten prematurely. + Stdio::Fd(ref fd) => { + if fd.raw() <= 2 { + Ok((ChildStdio::Owned(fd.duplicate()?), None)) + } else { + Ok((ChildStdio::Explicit(fd.raw()), None)) + } + } + + Stdio::MakePipe => { + let (reader, writer) = pipe::anon_pipe()?; + let (ours, theirs) = if readable { + (writer, reader) + } else { + (reader, writer) + }; + Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) + } + + Stdio::Null => { + let mut opts = OpenOptions::new(); + opts.read(readable); + opts.write(!readable); + let fd = File::open(&Path::new("null:"), &opts)?; + Ok((ChildStdio::Owned(fd.into_fd()), None)) + } + } + } +} + +impl ChildStdio { + fn fd(&self) -> Option<usize> { + match *self { + ChildStdio::Inherit => None, + ChildStdio::Explicit(fd) => Some(fd), + ChildStdio::Owned(ref fd) => Some(fd.raw()), + } + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.program)?; + for arg in &self.args { + write!(f, " {:?}", arg)?; + } + Ok(()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// Unix exit statuses +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(i32); + +impl ExitStatus { + fn exited(&self) -> bool { + self.0 & 0x7F == 0 + } + + pub fn success(&self) -> bool { + self.code() == Some(0) + } + + pub fn code(&self) -> Option<i32> { + if self.exited() { + Some((self.0 >> 8) & 0xFF) + } else { + None + } + } + + pub fn signal(&self) -> Option<i32> { + if !self.exited() { + Some(self.0 & 0x7F) + } else { + None + } + } +} + +impl From<i32> for ExitStatus { + fn from(a: i32) -> ExitStatus { + ExitStatus(a) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(code) = self.code() { + write!(f, "exit code: {}", code) + } else { + let signal = self.signal().unwrap(); + write!(f, "signal: {}", signal) + } + } +} + +/// The unique id of the process (this should never be negative). +pub struct Process { + pid: usize, + status: Option<ExitStatus>, +} + +impl Process { + pub fn id(&self) -> u32 { + self.pid as u32 + } + + pub fn kill(&mut self) -> io::Result<()> { + // If we've already waited on this process then the pid can be recycled + // and used for another process, and we probably shouldn't be killing + // random processes, so just return an error. + if self.status.is_some() { + Err(Error::new(ErrorKind::InvalidInput, + "invalid argument: can't kill an exited process")) + } else { + cvt(syscall::kill(self.pid, syscall::SIGKILL))?; + Ok(()) + } + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + if let Some(status) = self.status { + return Ok(status) + } + let mut status = 0; + cvt(syscall::waitpid(self.pid, &mut status, 0))?; + self.status = Some(ExitStatus(status as i32)); + Ok(ExitStatus(status as i32)) + } +} diff --git a/src/libstd/sys/redox/rand.rs b/src/libstd/sys/redox/rand.rs new file mode 100644 index 00000000000..eb28eca38bc --- /dev/null +++ b/src/libstd/sys/redox/rand.rs @@ -0,0 +1,57 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use rand::Rng; + +// FIXME: Use rand: +pub struct OsRng { + state: [u64; 2] +} + +impl OsRng { + /// Create a new `OsRng`. + pub fn new() -> io::Result<OsRng> { + Ok(OsRng { + state: [0xBADF00D1, 0xDEADBEEF] + }) + } +} + +impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + fn next_u64(&mut self) -> u64 { + // Store the first and second part. + let mut x = self.state[0]; + let y = self.state[1]; + + // Put the second part into the first slot. + self.state[0] = y; + // Twist the first slot. + x ^= x << 23; + // Update the second slot. + self.state[1] = x ^ y ^ (x >> 17) ^ (y >> 26); + + // Generate the final integer. + self.state[1].wrapping_add(y) + + } + fn fill_bytes(&mut self, buf: &mut [u8]) { + for chunk in buf.chunks_mut(8) { + let mut rand: u64 = self.next_u64(); + for b in chunk.iter_mut() { + *b = rand as u8; + rand = rand >> 8; + } + } + } +} diff --git a/src/libstd/sys/redox/rwlock.rs b/src/libstd/sys/redox/rwlock.rs new file mode 100644 index 00000000000..d74b614ba47 --- /dev/null +++ b/src/libstd/sys/redox/rwlock.rs @@ -0,0 +1,61 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::mutex::Mutex; + +pub struct RWLock { + mutex: Mutex +} + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { + mutex: Mutex::new() + } + } + + #[inline] + pub unsafe fn read(&self) { + self.mutex.lock(); + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + self.mutex.try_lock() + } + + #[inline] + pub unsafe fn write(&self) { + self.mutex.lock(); + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + self.mutex.try_lock() + } + + #[inline] + pub unsafe fn read_unlock(&self) { + self.mutex.unlock(); + } + + #[inline] + pub unsafe fn write_unlock(&self) { + self.mutex.unlock(); + } + + #[inline] + pub unsafe fn destroy(&self) { + self.mutex.destroy(); + } +} diff --git a/src/libstd/sys/redox/stack_overflow.rs b/src/libstd/sys/redox/stack_overflow.rs new file mode 100644 index 00000000000..760fe06c57f --- /dev/null +++ b/src/libstd/sys/redox/stack_overflow.rs @@ -0,0 +1,27 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg_attr(test, allow(dead_code))] + +pub struct Handler; + +impl Handler { + pub unsafe fn new() -> Handler { + Handler + } +} + +pub unsafe fn init() { + +} + +pub unsafe fn cleanup() { + +} diff --git a/src/libstd/sys/redox/stdio.rs b/src/libstd/sys/redox/stdio.rs new file mode 100644 index 00000000000..607eef051d6 --- /dev/null +++ b/src/libstd/sys/redox/stdio.rs @@ -0,0 +1,81 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::{cvt, syscall}; +use sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +impl Stdin { + pub fn new() -> io::Result<Stdin> { Ok(Stdin(())) } + + pub fn read(&self, data: &mut [u8]) -> io::Result<usize> { + let fd = FileDesc::new(0); + let ret = fd.read(data); + fd.into_raw(); + ret + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + let fd = FileDesc::new(0); + let ret = fd.read_to_end(buf); + fd.into_raw(); + ret + } +} + +impl Stdout { + pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + let fd = FileDesc::new(1); + let ret = fd.write(data); + fd.into_raw(); + ret + } + + pub fn flush(&self) -> io::Result<()> { + cvt(syscall::fsync(1)).and(Ok(())) + } +} + +impl Stderr { + pub fn new() -> io::Result<Stderr> { Ok(Stderr(())) } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + let fd = FileDesc::new(2); + let ret = fd.write(data); + fd.into_raw(); + ret + } + + pub fn flush(&self) -> io::Result<()> { + cvt(syscall::fsync(2)).and(Ok(())) + } +} + +// FIXME: right now this raw stderr handle is used in a few places because +// std::io::stderr_raw isn't exposed, but once that's exposed this impl +// should go away +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + Stderr::write(self, data) + } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } +} + +pub const EBADF_ERR: i32 = ::sys::syscall::EBADF; +pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; diff --git a/src/libstd/sys/redox/syscall/arch/arm.rs b/src/libstd/sys/redox/syscall/arch/arm.rs new file mode 100644 index 00000000000..9fb3961486d --- /dev/null +++ b/src/libstd/sys/redox/syscall/arch/arm.rs @@ -0,0 +1,83 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::error::{Error, Result}; + +pub unsafe fn syscall0(mut a: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a) + : "memory" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b) + : "memory" + : "volatile"); + + Error::demux(a) +} + +// Clobbers all registers - special for clone +pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b) + : "memory", "r0", "r1", "r2", "r3", "r4" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b), "{r1}"(c) + : "memory" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d) + : "memory" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d), "{r3}"(e) + : "memory" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) + -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d), "{r3}"(e), "{r4}"(f) + : "memory" + : "volatile"); + + Error::demux(a) +} diff --git a/src/libstd/sys/redox/syscall/arch/x86.rs b/src/libstd/sys/redox/syscall/arch/x86.rs new file mode 100644 index 00000000000..724a6b927f4 --- /dev/null +++ b/src/libstd/sys/redox/syscall/arch/x86.rs @@ -0,0 +1,83 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::error::{Error, Result}; + +pub unsafe fn syscall0(mut a: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +// Clobbers all registers - special for clone +pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b) + : "memory", "ebx", "ecx", "edx", "esi", "edi" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b), "{ecx}"(c) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d), "{esi}"(e) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) + -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d), "{esi}"(e), "{edi}"(f) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} diff --git a/src/libstd/sys/redox/syscall/arch/x86_64.rs b/src/libstd/sys/redox/syscall/arch/x86_64.rs new file mode 100644 index 00000000000..a321c31f207 --- /dev/null +++ b/src/libstd/sys/redox/syscall/arch/x86_64.rs @@ -0,0 +1,84 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::error::{Error, Result}; + +pub unsafe fn syscall0(mut a: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +// Clobbers all registers - special for clone +pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b) + : "memory", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", + "r9", "r10", "r11", "r12", "r13", "r14", "r15" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b), "{rcx}"(c) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d), "{rsi}"(e) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) + -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d), "{rsi}"(e), "{rdi}"(f) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} diff --git a/src/libstd/sys/redox/syscall/call.rs b/src/libstd/sys/redox/syscall/call.rs new file mode 100644 index 00000000000..f58c240f31e --- /dev/null +++ b/src/libstd/sys/redox/syscall/call.rs @@ -0,0 +1,300 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::arch::*; +use super::data::{Stat, StatVfs, TimeSpec}; +use super::error::Result; +use super::number::*; + +use core::mem; + +/// Set the end of the process's heap +/// +/// When `addr` is `0`, this function will return the current break. +/// +/// When `addr` is nonzero, this function will attempt to set the end of the process's +/// heap to `addr` and return the new program break. The new program break should be +/// checked by the allocator, it may not be exactly `addr`, as it may be aligned to a page +/// boundary. +/// +/// On error, `Err(ENOMEM)` will be returned indicating that no memory is available +pub unsafe fn brk(addr: usize) -> Result<usize> { + syscall1(SYS_BRK, addr) +} + +/// Change the process's working directory +/// +/// This function will attempt to set the process's working directory to `path`, which can be +/// either a relative, scheme relative, or absolute path. +/// +/// On success, `Ok(0)` will be returned. On error, one of the following errors will be returned. +/// +/// # Errors +/// +/// * `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 +/// * `ENOENT` - `path` does not exit +/// * `ENOTDIR` - `path` is not a directory +pub fn chdir(path: &str) -> Result<usize> { + unsafe { syscall2(SYS_CHDIR, path.as_ptr() as usize, path.len()) } +} + +pub fn chmod(path: &str, mode: usize) -> Result<usize> { + unsafe { syscall3(SYS_CHMOD, path.as_ptr() as usize, path.len(), mode) } +} + +/// Produce a fork of the current process, or a new process thread +pub unsafe fn clone(flags: usize) -> Result<usize> { + syscall1_clobber(SYS_CLONE, flags) +} + +/// Close a file +pub fn close(fd: usize) -> Result<usize> { + unsafe { syscall1(SYS_CLOSE, fd) } +} + +/// Get the current system time +pub fn clock_gettime(clock: usize, tp: &mut TimeSpec) -> Result<usize> { + unsafe { syscall2(SYS_CLOCK_GETTIME, clock, tp as *mut TimeSpec as usize) } +} + +/// Copy and transform a file descriptor +pub fn dup(fd: usize, buf: &[u8]) -> Result<usize> { + unsafe { syscall3(SYS_DUP, fd, buf.as_ptr() as usize, buf.len()) } +} + +/// Replace the current process with a new executable +pub fn execve(path: &str, args: &[[usize; 2]]) -> Result<usize> { + unsafe { syscall4(SYS_EXECVE, path.as_ptr() as usize, path.len(), + args.as_ptr() as usize, args.len()) } +} + +/// Exit the current process +pub fn exit(status: usize) -> Result<usize> { + unsafe { syscall1(SYS_EXIT, status) } +} + +/// Register a file for event-based I/O +pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> { + unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) } +} + +/// Register a file for event-based I/O +pub fn fevent(fd: usize, flags: usize) -> Result<usize> { + unsafe { syscall2(SYS_FEVENT, fd, flags) } +} + +/// Map a file into memory +pub unsafe fn fmap(fd: usize, offset: usize, size: usize) -> Result<usize> { + syscall3(SYS_FMAP, fd, offset, size) +} + +/// Unmap a memory-mapped file +pub unsafe fn funmap(addr: usize) -> Result<usize> { + syscall1(SYS_FUNMAP, addr) +} + +/// Retrieve the canonical path of a file +pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> { + unsafe { syscall3(SYS_FPATH, fd, buf.as_mut_ptr() as usize, buf.len()) } +} + +/// Get metadata about a file +pub fn fstat(fd: usize, stat: &mut Stat) -> Result<usize> { + unsafe { syscall3(SYS_FSTAT, fd, stat as *mut Stat as usize, mem::size_of::<Stat>()) } +} + +/// Get metadata about a filesystem +pub fn fstatvfs(fd: usize, stat: &mut StatVfs) -> Result<usize> { + unsafe { syscall3(SYS_FSTATVFS, fd, stat as *mut StatVfs as usize, mem::size_of::<StatVfs>()) } +} + +/// Sync a file descriptor to its underlying medium +pub fn fsync(fd: usize) -> Result<usize> { + unsafe { syscall1(SYS_FSYNC, fd) } +} + +/// Truncate or extend a file to a specified length +pub fn ftruncate(fd: usize, len: usize) -> Result<usize> { + unsafe { syscall2(SYS_FTRUNCATE, fd, len) } +} + +/// Fast userspace mutex +pub unsafe fn futex(addr: *mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) + -> Result<usize> { + syscall5(SYS_FUTEX, addr as usize, op, (val as isize) as usize, val2, addr2 as usize) +} + +/// Get the current working directory +pub fn getcwd(buf: &mut [u8]) -> Result<usize> { + unsafe { syscall2(SYS_GETCWD, buf.as_mut_ptr() as usize, buf.len()) } +} + +/// Get the effective group ID +pub fn getegid() -> Result<usize> { + unsafe { syscall0(SYS_GETEGID) } +} + +/// Get the effective namespace +pub fn getens() -> Result<usize> { + unsafe { syscall0(SYS_GETENS) } +} + +/// Get the effective user ID +pub fn geteuid() -> Result<usize> { + unsafe { syscall0(SYS_GETEUID) } +} + +/// Get the current group ID +pub fn getgid() -> Result<usize> { + unsafe { syscall0(SYS_GETGID) } +} + +/// Get the current namespace +pub fn getns() -> Result<usize> { + unsafe { syscall0(SYS_GETNS) } +} + +/// Get the current process ID +pub fn getpid() -> Result<usize> { + unsafe { syscall0(SYS_GETPID) } +} + +/// Get the current user ID +pub fn getuid() -> Result<usize> { + unsafe { syscall0(SYS_GETUID) } +} + +/// Set the I/O privilege level +pub unsafe fn iopl(level: usize) -> Result<usize> { + syscall1(SYS_IOPL, level) +} + +/// Send a signal `sig` to the process identified by `pid` +pub fn kill(pid: usize, sig: usize) -> Result<usize> { + unsafe { syscall2(SYS_KILL, pid, sig) } +} + +/// Create a link to a file +pub unsafe fn link(old: *const u8, new: *const u8) -> Result<usize> { + syscall2(SYS_LINK, old as usize, new as usize) +} + +/// Seek to `offset` bytes in a file descriptor +pub fn lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> { + unsafe { syscall3(SYS_LSEEK, fd, offset as usize, whence) } +} + +/// Make a new scheme namespace +pub fn mkns(schemes: &[[usize; 2]]) -> Result<usize> { + unsafe { syscall2(SYS_MKNS, schemes.as_ptr() as usize, schemes.len()) } +} + +/// Sleep for the time specified in `req` +pub fn nanosleep(req: &TimeSpec, rem: &mut TimeSpec) -> Result<usize> { + unsafe { syscall2(SYS_NANOSLEEP, req as *const TimeSpec as usize, + rem as *mut TimeSpec as usize) } +} + +/// Open a file +pub fn open(path: &str, flags: usize) -> Result<usize> { + unsafe { syscall3(SYS_OPEN, path.as_ptr() as usize, path.len(), flags) } +} + +/// Allocate pages, linearly in physical memory +pub unsafe fn physalloc(size: usize) -> Result<usize> { + syscall1(SYS_PHYSALLOC, size) +} + +/// Free physically allocated pages +pub unsafe fn physfree(physical_address: usize, size: usize) -> Result<usize> { + syscall2(SYS_PHYSFREE, physical_address, size) +} + +/// Map physical memory to virtual memory +pub unsafe fn physmap(physical_address: usize, size: usize, flags: usize) -> Result<usize> { + syscall3(SYS_PHYSMAP, physical_address, size, flags) +} + +/// Unmap previously mapped physical memory +pub unsafe fn physunmap(virtual_address: usize) -> Result<usize> { + syscall1(SYS_PHYSUNMAP, virtual_address) +} + +/// Create a pair of file descriptors referencing the read and write ends of a pipe +pub fn pipe2(fds: &mut [usize; 2], flags: usize) -> Result<usize> { + unsafe { syscall2(SYS_PIPE2, fds.as_ptr() as usize, flags) } +} + +/// Read from a file descriptor into a buffer +pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> { + unsafe { syscall3(SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) } +} + +/// Remove a directory +pub fn rmdir(path: &str) -> Result<usize> { + unsafe { syscall2(SYS_RMDIR, path.as_ptr() as usize, path.len()) } +} + +/// Set the current process group IDs +pub fn setregid(rgid: usize, egid: usize) -> Result<usize> { + unsafe { syscall2(SYS_SETREGID, rgid, egid) } +} + +/// Make a new scheme namespace +pub fn setrens(rns: usize, ens: usize) -> Result<usize> { + unsafe { syscall2(SYS_SETRENS, rns, ens) } +} + +/// Set the current process user IDs +pub fn setreuid(ruid: usize, euid: usize) -> Result<usize> { + unsafe { syscall2(SYS_SETREUID, ruid, euid) } +} + +/// Remove a file +pub fn unlink(path: &str) -> Result<usize> { + unsafe { syscall2(SYS_UNLINK, path.as_ptr() as usize, path.len()) } +} + +/// Convert a virtual address to a physical one +pub unsafe fn virttophys(virtual_address: usize) -> Result<usize> { + syscall1(SYS_VIRTTOPHYS, virtual_address) +} + +/// Check if a child process has exited or received a signal +pub fn waitpid(pid: usize, status: &mut usize, options: usize) -> Result<usize> { + unsafe { syscall3(SYS_WAITPID, pid, status as *mut usize as usize, options) } +} + +/// Write a buffer to a file descriptor +/// +/// The kernel will attempt to write the bytes in `buf` to the file descriptor `fd`, returning +/// either an `Err`, explained below, or `Ok(count)` where `count` is the number of bytes which +/// were written. +/// +/// # Errors +/// +/// * `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 +/// * `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<usize> { + unsafe { syscall3(SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) } +} + +/// Yield the process's time slice to the kernel +/// +/// This function will return Ok(0) on success +pub fn sched_yield() -> Result<usize> { + unsafe { syscall0(SYS_YIELD) } +} diff --git a/src/libstd/sys/redox/syscall/data.rs b/src/libstd/sys/redox/syscall/data.rs new file mode 100644 index 00000000000..ac3946672a3 --- /dev/null +++ b/src/libstd/sys/redox/syscall/data.rs @@ -0,0 +1,86 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::ops::{Deref, DerefMut}; +use core::{mem, slice}; + +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct Stat { + pub st_dev: u64, + pub st_ino: u64, + pub st_mode: u16, + pub st_nlink: u32, + pub st_uid: u32, + pub st_gid: u32, + pub st_size: u64, + pub st_blksize: u32, + pub st_blocks: u64, + pub st_mtime: u64, + pub st_mtime_nsec: u32, + pub st_atime: u64, + pub st_atime_nsec: u32, + pub st_ctime: u64, + pub st_ctime_nsec: u32, +} + +impl Deref for Stat { + type Target = [u8]; + fn deref(&self) -> &[u8] { + unsafe { + slice::from_raw_parts(self as *const Stat as *const u8, + mem::size_of::<Stat>()) as &[u8] + } + } +} + +impl DerefMut for Stat { + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { + slice::from_raw_parts_mut(self as *mut Stat as *mut u8, + mem::size_of::<Stat>()) as &mut [u8] + } + } +} + +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct StatVfs { + pub f_bsize: u32, + pub f_blocks: u64, + pub f_bfree: u64, + pub f_bavail: u64, +} + +impl Deref for StatVfs { + type Target = [u8]; + fn deref(&self) -> &[u8] { + unsafe { + slice::from_raw_parts(self as *const StatVfs as *const u8, + mem::size_of::<StatVfs>()) as &[u8] + } + } +} + +impl DerefMut for StatVfs { + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { + slice::from_raw_parts_mut(self as *mut StatVfs as *mut u8, + mem::size_of::<StatVfs>()) as &mut [u8] + } + } +} + +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct TimeSpec { + pub tv_sec: i64, + pub tv_nsec: i32, +} diff --git a/src/libstd/sys/redox/syscall/error.rs b/src/libstd/sys/redox/syscall/error.rs new file mode 100644 index 00000000000..d8d78d55016 --- /dev/null +++ b/src/libstd/sys/redox/syscall/error.rs @@ -0,0 +1,325 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::{fmt, result}; + +#[derive(Eq, PartialEq)] +pub struct Error { + pub errno: i32, +} + +pub type Result<T> = result::Result<T, Error>; + +impl Error { + pub fn new(errno: i32) -> Error { + Error { errno: errno } + } + + pub fn mux(result: Result<usize>) -> usize { + match result { + Ok(value) => value, + Err(error) => -error.errno as usize, + } + } + + pub fn demux(value: usize) -> Result<usize> { + let errno = -(value as i32); + if errno >= 1 && errno < STR_ERROR.len() as i32 { + Err(Error::new(errno)) + } else { + Ok(value) + } + } + + pub fn text(&self) -> &str { + if let Some(description) = STR_ERROR.get(self.errno as usize) { + description + } else { + "Unknown Error" + } + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + f.write_str(self.text()) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + f.write_str(self.text()) + } +} + +pub const EPERM: i32 = 1; /* Operation not permitted */ +pub const ENOENT: i32 = 2; /* No such file or directory */ +pub const ESRCH: i32 = 3; /* No such process */ +pub const EINTR: i32 = 4; /* Interrupted system call */ +pub const EIO: i32 = 5; /* I/O error */ +pub const ENXIO: i32 = 6; /* No such device or address */ +pub const E2BIG: i32 = 7; /* Argument list too long */ +pub const ENOEXEC: i32 = 8; /* Exec format error */ +pub const EBADF: i32 = 9; /* Bad file number */ +pub const ECHILD: i32 = 10; /* No child processes */ +pub const EAGAIN: i32 = 11; /* Try again */ +pub const ENOMEM: i32 = 12; /* Out of memory */ +pub const EACCES: i32 = 13; /* Permission denied */ +pub const EFAULT: i32 = 14; /* Bad address */ +pub const ENOTBLK: i32 = 15; /* Block device required */ +pub const EBUSY: i32 = 16; /* Device or resource busy */ +pub const EEXIST: i32 = 17; /* File exists */ +pub const EXDEV: i32 = 18; /* Cross-device link */ +pub const ENODEV: i32 = 19; /* No such device */ +pub const ENOTDIR: i32 = 20; /* Not a directory */ +pub const EISDIR: i32 = 21; /* Is a directory */ +pub const EINVAL: i32 = 22; /* Invalid argument */ +pub const ENFILE: i32 = 23; /* File table overflow */ +pub const EMFILE: i32 = 24; /* Too many open files */ +pub const ENOTTY: i32 = 25; /* Not a typewriter */ +pub const ETXTBSY: i32 = 26; /* Text file busy */ +pub const EFBIG: i32 = 27; /* File too large */ +pub const ENOSPC: i32 = 28; /* No space left on device */ +pub const ESPIPE: i32 = 29; /* Illegal seek */ +pub const EROFS: i32 = 30; /* Read-only file system */ +pub const EMLINK: i32 = 31; /* Too many links */ +pub const EPIPE: i32 = 32; /* Broken pipe */ +pub const EDOM: i32 = 33; /* Math argument out of domain of func */ +pub const ERANGE: i32 = 34; /* Math result not representable */ +pub const EDEADLK: i32 = 35; /* Resource deadlock would occur */ +pub const ENAMETOOLONG: i32 = 36; /* File name too long */ +pub const ENOLCK: i32 = 37; /* No record locks available */ +pub const ENOSYS: i32 = 38; /* Function not implemented */ +pub const ENOTEMPTY: i32 = 39; /* Directory not empty */ +pub const ELOOP: i32 = 40; /* Too many symbolic links encountered */ +pub const EWOULDBLOCK: i32 = 41; /* Operation would block */ +pub const ENOMSG: i32 = 42; /* No message of desired type */ +pub const EIDRM: i32 = 43; /* Identifier removed */ +pub const ECHRNG: i32 = 44; /* Channel number out of range */ +pub const EL2NSYNC: i32 = 45; /* Level 2 not synchronized */ +pub const EL3HLT: i32 = 46; /* Level 3 halted */ +pub const EL3RST: i32 = 47; /* Level 3 reset */ +pub const ELNRNG: i32 = 48; /* Link number out of range */ +pub const EUNATCH: i32 = 49; /* Protocol driver not attached */ +pub const ENOCSI: i32 = 50; /* No CSI structure available */ +pub const EL2HLT: i32 = 51; /* Level 2 halted */ +pub const EBADE: i32 = 52; /* Invalid exchange */ +pub const EBADR: i32 = 53; /* Invalid request descriptor */ +pub const EXFULL: i32 = 54; /* Exchange full */ +pub const ENOANO: i32 = 55; /* No anode */ +pub const EBADRQC: i32 = 56; /* Invalid request code */ +pub const EBADSLT: i32 = 57; /* Invalid slot */ +pub const EDEADLOCK: i32 = 58; /* Resource deadlock would occur */ +pub const EBFONT: i32 = 59; /* Bad font file format */ +pub const ENOSTR: i32 = 60; /* Device not a stream */ +pub const ENODATA: i32 = 61; /* No data available */ +pub const ETIME: i32 = 62; /* Timer expired */ +pub const ENOSR: i32 = 63; /* Out of streams resources */ +pub const ENONET: i32 = 64; /* Machine is not on the network */ +pub const ENOPKG: i32 = 65; /* Package not installed */ +pub const EREMOTE: i32 = 66; /* Object is remote */ +pub const ENOLINK: i32 = 67; /* Link has been severed */ +pub const EADV: i32 = 68; /* Advertise error */ +pub const ESRMNT: i32 = 69; /* Srmount error */ +pub const ECOMM: i32 = 70; /* Communication error on send */ +pub const EPROTO: i32 = 71; /* Protocol error */ +pub const EMULTIHOP: i32 = 72; /* Multihop attempted */ +pub const EDOTDOT: i32 = 73; /* RFS specific error */ +pub const EBADMSG: i32 = 74; /* Not a data message */ +pub const EOVERFLOW: i32 = 75; /* Value too large for defined data type */ +pub const ENOTUNIQ: i32 = 76; /* Name not unique on network */ +pub const EBADFD: i32 = 77; /* File descriptor in bad state */ +pub const EREMCHG: i32 = 78; /* Remote address changed */ +pub const ELIBACC: i32 = 79; /* Can not access a needed shared library */ +pub const ELIBBAD: i32 = 80; /* Accessing a corrupted shared library */ +pub const ELIBSCN: i32 = 81; /* .lib section in a.out corrupted */ +pub const ELIBMAX: i32 = 82; /* Attempting to link in too many shared libraries */ +pub const ELIBEXEC: i32 = 83; /* Cannot exec a shared library directly */ +pub const EILSEQ: i32 = 84; /* Illegal byte sequence */ +pub const ERESTART: i32 = 85; /* Interrupted system call should be restarted */ +pub const ESTRPIPE: i32 = 86; /* Streams pipe error */ +pub const EUSERS: i32 = 87; /* Too many users */ +pub const ENOTSOCK: i32 = 88; /* Socket operation on non-socket */ +pub const EDESTADDRREQ: i32 = 89; /* Destination address required */ +pub const EMSGSIZE: i32 = 90; /* Message too long */ +pub const EPROTOTYPE: i32 = 91; /* Protocol wrong type for socket */ +pub const ENOPROTOOPT: i32 = 92; /* Protocol not available */ +pub const EPROTONOSUPPORT: i32 = 93; /* Protocol not supported */ +pub const ESOCKTNOSUPPORT: i32 = 94; /* Socket type not supported */ +pub const EOPNOTSUPP: i32 = 95; /* Operation not supported on transport endpoint */ +pub const EPFNOSUPPORT: i32 = 96; /* Protocol family not supported */ +pub const EAFNOSUPPORT: i32 = 97; /* Address family not supported by protocol */ +pub const EADDRINUSE: i32 = 98; /* Address already in use */ +pub const EADDRNOTAVAIL: i32 = 99; /* Cannot assign requested address */ +pub const ENETDOWN: i32 = 100; /* Network is down */ +pub const ENETUNREACH: i32 = 101; /* Network is unreachable */ +pub const ENETRESET: i32 = 102; /* Network dropped connection because of reset */ +pub const ECONNABORTED: i32 = 103; /* Software caused connection abort */ +pub const ECONNRESET: i32 = 104; /* Connection reset by peer */ +pub const ENOBUFS: i32 = 105; /* No buffer space available */ +pub const EISCONN: i32 = 106; /* Transport endpoint is already connected */ +pub const ENOTCONN: i32 = 107; /* Transport endpoint is not connected */ +pub const ESHUTDOWN: i32 = 108; /* Cannot send after transport endpoint shutdown */ +pub const ETOOMANYREFS: i32 = 109; /* Too many references: cannot splice */ +pub const ETIMEDOUT: i32 = 110; /* Connection timed out */ +pub const ECONNREFUSED: i32 = 111; /* Connection refused */ +pub const EHOSTDOWN: i32 = 112; /* Host is down */ +pub const EHOSTUNREACH: i32 = 113; /* No route to host */ +pub const EALREADY: i32 = 114; /* Operation already in progress */ +pub const EINPROGRESS: i32 = 115; /* Operation now in progress */ +pub const ESTALE: i32 = 116; /* Stale NFS file handle */ +pub const EUCLEAN: i32 = 117; /* Structure needs cleaning */ +pub const ENOTNAM: i32 = 118; /* Not a XENIX named type file */ +pub const ENAVAIL: i32 = 119; /* No XENIX semaphores available */ +pub const EISNAM: i32 = 120; /* Is a named type file */ +pub const EREMOTEIO: i32 = 121; /* Remote I/O error */ +pub const EDQUOT: i32 = 122; /* Quota exceeded */ +pub const ENOMEDIUM: i32 = 123; /* No medium found */ +pub const EMEDIUMTYPE: i32 = 124; /* Wrong medium type */ +pub const ECANCELED: i32 = 125; /* Operation Canceled */ +pub const ENOKEY: i32 = 126; /* Required key not available */ +pub const EKEYEXPIRED: i32 = 127; /* Key has expired */ +pub const EKEYREVOKED: i32 = 128; /* Key has been revoked */ +pub const EKEYREJECTED: i32 = 129; /* Key was rejected by service */ +pub const EOWNERDEAD: i32 = 130; /* Owner died */ +pub const ENOTRECOVERABLE: i32 = 131; /* State not recoverable */ + +pub static STR_ERROR: [&'static str; 132] = ["Success", + "Operation not permitted", + "No such file or directory", + "No such process", + "Interrupted system call", + "I/O error", + "No such device or address", + "Argument list too long", + "Exec format error", + "Bad file number", + "No child processes", + "Try again", + "Out of memory", + "Permission denied", + "Bad address", + "Block device required", + "Device or resource busy", + "File exists", + "Cross-device link", + "No such device", + "Not a directory", + "Is a directory", + "Invalid argument", + "File table overflow", + "Too many open files", + "Not a typewriter", + "Text file busy", + "File too large", + "No space left on device", + "Illegal seek", + "Read-only file system", + "Too many links", + "Broken pipe", + "Math argument out of domain of func", + "Math result not representable", + "Resource deadlock would occur", + "File name too long", + "No record locks available", + "Function not implemented", + "Directory not empty", + "Too many symbolic links encountered", + "Operation would block", + "No message of desired type", + "Identifier removed", + "Channel number out of range", + "Level 2 not synchronized", + "Level 3 halted", + "Level 3 reset", + "Link number out of range", + "Protocol driver not attached", + "No CSI structure available", + "Level 2 halted", + "Invalid exchange", + "Invalid request descriptor", + "Exchange full", + "No anode", + "Invalid request code", + "Invalid slot", + "Resource deadlock would occur", + "Bad font file format", + "Device not a stream", + "No data available", + "Timer expired", + "Out of streams resources", + "Machine is not on the network", + "Package not installed", + "Object is remote", + "Link has been severed", + "Advertise error", + "Srmount error", + "Communication error on send", + "Protocol error", + "Multihop attempted", + "RFS specific error", + "Not a data message", + "Value too large for defined data type", + "Name not unique on network", + "File descriptor in bad state", + "Remote address changed", + "Can not access a needed shared library", + "Accessing a corrupted shared library", + ".lib section in a.out corrupted", + "Attempting to link in too many shared libraries", + "Cannot exec a shared library directly", + "Illegal byte sequence", + "Interrupted system call should be restarted", + "Streams pipe error", + "Too many users", + "Socket operation on non-socket", + "Destination address required", + "Message too long", + "Protocol wrong type for socket", + "Protocol not available", + "Protocol not supported", + "Socket type not supported", + "Operation not supported on transport endpoint", + "Protocol family not supported", + "Address family not supported by protocol", + "Address already in use", + "Cannot assign requested address", + "Network is down", + "Network is unreachable", + "Network dropped connection because of reset", + "Software caused connection abort", + "Connection reset by peer", + "No buffer space available", + "Transport endpoint is already connected", + "Transport endpoint is not connected", + "Cannot send after transport endpoint shutdown", + "Too many references: cannot splice", + "Connection timed out", + "Connection refused", + "Host is down", + "No route to host", + "Operation already in progress", + "Operation now in progress", + "Stale NFS file handle", + "Structure needs cleaning", + "Not a XENIX named type file", + "No XENIX semaphores available", + "Is a named type file", + "Remote I/O error", + "Quota exceeded", + "No medium found", + "Wrong medium type", + "Operation Canceled", + "Required key not available", + "Key has expired", + "Key has been revoked", + "Key was rejected by service", + "Owner died", + "State not recoverable"]; diff --git a/src/libstd/sys/redox/syscall/flag.rs b/src/libstd/sys/redox/syscall/flag.rs new file mode 100644 index 00000000000..9f0d3e6f779 --- /dev/null +++ b/src/libstd/sys/redox/syscall/flag.rs @@ -0,0 +1,94 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub const CLONE_VM: usize = 0x100; +pub const CLONE_FS: usize = 0x200; +pub const CLONE_FILES: usize = 0x400; +pub const CLONE_VFORK: usize = 0x4000; + +pub const CLOCK_REALTIME: usize = 1; +pub const CLOCK_MONOTONIC: usize = 4; + +pub const EVENT_NONE: usize = 0; +pub const EVENT_READ: usize = 1; +pub const EVENT_WRITE: usize = 2; + +pub const F_GETFL: usize = 1; +pub const F_SETFL: usize = 2; + +pub const FUTEX_WAIT: usize = 0; +pub const FUTEX_WAKE: usize = 1; +pub const FUTEX_REQUEUE: usize = 2; + +pub const MAP_WRITE: usize = 1; +pub const MAP_WRITE_COMBINE: usize = 2; + +pub const MODE_TYPE: u16 = 0xF000; +pub const MODE_DIR: u16 = 0x4000; +pub const MODE_FILE: u16 = 0x8000; + +pub const MODE_PERM: u16 = 0x0FFF; +pub const MODE_SETUID: u16 = 0o4000; +pub const MODE_SETGID: u16 = 0o2000; + +pub const O_RDONLY: usize = 0x0001_0000; +pub const O_WRONLY: usize = 0x0002_0000; +pub const O_RDWR: usize = 0x0003_0000; +pub const O_NONBLOCK: usize = 0x0004_0000; +pub const O_APPEND: usize = 0x0008_0000; +pub const O_SHLOCK: usize = 0x0010_0000; +pub const O_EXLOCK: usize = 0x0020_0000; +pub const O_ASYNC: usize = 0x0040_0000; +pub const O_FSYNC: usize = 0x0080_0000; +pub const O_CLOEXEC: usize = 0x0100_0000; +pub const O_CREAT: usize = 0x0200_0000; +pub const O_TRUNC: usize = 0x0400_0000; +pub const O_EXCL: usize = 0x0800_0000; +pub const O_DIRECTORY: usize = 0x1000_0000; +pub const O_STAT: usize = 0x2000_0000; +pub const O_ACCMODE: usize = O_RDONLY | O_WRONLY | O_RDWR; + +pub const SEEK_SET: usize = 0; +pub const SEEK_CUR: usize = 1; +pub const SEEK_END: usize = 2; + +pub const SIGHUP: usize = 1; +pub const SIGINT: usize = 2; +pub const SIGQUIT: usize = 3; +pub const SIGILL: usize = 4; +pub const SIGTRAP: usize = 5; +pub const SIGABRT: usize = 6; +pub const SIGBUS: usize = 7; +pub const SIGFPE: usize = 8; +pub const SIGKILL: usize = 9; +pub const SIGUSR1: usize = 10; +pub const SIGSEGV: usize = 11; +pub const SIGUSR2: usize = 12; +pub const SIGPIPE: usize = 13; +pub const SIGALRM: usize = 14; +pub const SIGTERM: usize = 15; +pub const SIGSTKFLT: usize= 16; +pub const SIGCHLD: usize = 17; +pub const SIGCONT: usize = 18; +pub const SIGSTOP: usize = 19; +pub const SIGTSTP: usize = 20; +pub const SIGTTIN: usize = 21; +pub const SIGTTOU: usize = 22; +pub const SIGURG: usize = 23; +pub const SIGXCPU: usize = 24; +pub const SIGXFSZ: usize = 25; +pub const SIGVTALRM: usize= 26; +pub const SIGPROF: usize = 27; +pub const SIGWINCH: usize = 28; +pub const SIGIO: usize = 29; +pub const SIGPWR: usize = 30; +pub const SIGSYS: usize = 31; + +pub const WNOHANG: usize = 1; diff --git a/src/libstd/sys/redox/syscall/mod.rs b/src/libstd/sys/redox/syscall/mod.rs new file mode 100644 index 00000000000..ce789c269a7 --- /dev/null +++ b/src/libstd/sys/redox/syscall/mod.rs @@ -0,0 +1,43 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::arch::*; +pub use self::call::*; +pub use self::data::*; +pub use self::error::*; +pub use self::flag::*; +pub use self::number::*; + +#[cfg(target_arch = "arm")] +#[path="arch/arm.rs"] +mod arch; + +#[cfg(target_arch = "x86")] +#[path="arch/x86.rs"] +mod arch; + +#[cfg(target_arch = "x86_64")] +#[path="arch/x86_64.rs"] +mod arch; + +/// Function definitions +pub mod call; + +/// Complex structures that are used for some system calls +pub mod data; + +/// All errors that can be generated by a system call +pub mod error; + +/// Flags used as an argument to many system calls +pub mod flag; + +/// Call numbers used by each system call +pub mod number; diff --git a/src/libstd/sys/redox/syscall/number.rs b/src/libstd/sys/redox/syscall/number.rs new file mode 100644 index 00000000000..358746cd20a --- /dev/null +++ b/src/libstd/sys/redox/syscall/number.rs @@ -0,0 +1,73 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub const SYS_CLASS: usize = 0xF000_0000; +pub const SYS_CLASS_PATH: usize=0x1000_0000; +pub const SYS_CLASS_FILE: usize=0x2000_0000; + +pub const SYS_ARG: usize = 0x0F00_0000; +pub const SYS_ARG_SLICE: usize =0x0100_0000; +pub const SYS_ARG_MSLICE: usize=0x0200_0000; +pub const SYS_ARG_PATH: usize = 0x0300_0000; + +pub const SYS_RET: usize = 0x00F0_0000; +pub const SYS_RET_FILE: usize = 0x0010_0000; + +pub const SYS_LINK: usize = SYS_CLASS_PATH | SYS_ARG_PATH | 9; +pub const SYS_OPEN: usize = SYS_CLASS_PATH | SYS_RET_FILE | 5; +pub const SYS_CHMOD: usize = SYS_CLASS_PATH | 15; +pub const SYS_RMDIR: usize = SYS_CLASS_PATH | 84; +pub const SYS_UNLINK: usize = SYS_CLASS_PATH | 10; + +pub const SYS_CLOSE: usize = SYS_CLASS_FILE | 6; +pub const SYS_DUP: usize = SYS_CLASS_FILE | SYS_RET_FILE | 41; +pub const SYS_READ: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 3; +pub const SYS_WRITE: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 4; +pub const SYS_LSEEK: usize = SYS_CLASS_FILE | 19; +pub const SYS_FCNTL: usize = SYS_CLASS_FILE | 55; +pub const SYS_FEVENT: usize = SYS_CLASS_FILE | 927; +pub const SYS_FMAP: usize = SYS_CLASS_FILE | 90; +pub const SYS_FUNMAP: usize = SYS_CLASS_FILE | 91; +pub const SYS_FPATH: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 928; +pub const SYS_FSTAT: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 28; +pub const SYS_FSTATVFS: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 100; +pub const SYS_FSYNC: usize = SYS_CLASS_FILE | 118; +pub const SYS_FTRUNCATE: usize =SYS_CLASS_FILE | 93; + +pub const SYS_BRK: usize = 45; +pub const SYS_CHDIR: usize = 12; +pub const SYS_CLOCK_GETTIME: usize = 265; +pub const SYS_CLONE: usize = 120; +pub const SYS_EXECVE: usize = 11; +pub const SYS_EXIT: usize = 1; +pub const SYS_FUTEX: usize = 240; +pub const SYS_GETCWD: usize = 183; +pub const SYS_GETEGID: usize = 202; +pub const SYS_GETENS: usize = 951; +pub const SYS_GETEUID: usize = 201; +pub const SYS_GETGID: usize = 200; +pub const SYS_GETNS: usize = 950; +pub const SYS_GETPID: usize = 20; +pub const SYS_GETUID: usize = 199; +pub const SYS_IOPL: usize = 110; +pub const SYS_KILL: usize = 37; +pub const SYS_MKNS: usize = 984; +pub const SYS_NANOSLEEP: usize =162; +pub const SYS_PHYSALLOC: usize =945; +pub const SYS_PHYSFREE: usize = 946; +pub const SYS_PHYSMAP: usize = 947; +pub const SYS_PHYSUNMAP: usize =948; +pub const SYS_VIRTTOPHYS: usize=949; +pub const SYS_PIPE2: usize = 331; +pub const SYS_SETREGID: usize = 204; +pub const SYS_SETRENS: usize = 952; +pub const SYS_SETREUID: usize = 203; +pub const SYS_WAITPID: usize = 7; +pub const SYS_YIELD: usize = 158; diff --git a/src/libstd/sys/redox/thread.rs b/src/libstd/sys/redox/thread.rs new file mode 100644 index 00000000000..b2c0e285f06 --- /dev/null +++ b/src/libstd/sys/redox/thread.rs @@ -0,0 +1,91 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use alloc::boxed::FnBox; +use ffi::CStr; +use io; +use mem; +use sys_common::thread::start_thread; +use sys::{cvt, syscall}; +use time::Duration; + +pub struct Thread { + id: usize, +} + +// Some platforms may have pthread_t as a pointer in which case we still want +// a thread to be Send/Sync +unsafe impl Send for Thread {} +unsafe impl Sync for Thread {} + +impl Thread { + pub unsafe fn new<'a>(_stack: usize, p: Box<FnBox() + 'a>) -> io::Result<Thread> { + let p = box p; + + let id = cvt(syscall::clone(syscall::CLONE_VM | syscall::CLONE_FS | syscall::CLONE_FILES))?; + if id == 0 { + start_thread(&*p as *const _ as *mut _); + let _ = syscall::exit(0); + panic!("thread failed to exit"); + } else { + mem::forget(p); + Ok(Thread { id: id }) + } + } + + pub fn yield_now() { + let ret = syscall::sched_yield().expect("failed to sched_yield"); + debug_assert_eq!(ret, 0); + } + + pub fn set_name(_name: &CStr) { + + } + + pub fn sleep(dur: Duration) { + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as i32; + + // If we're awoken with a signal then the return value will be -1 and + // nanosleep will fill in `ts` with the remaining time. + while secs > 0 || nsecs > 0 { + let req = syscall::TimeSpec { + tv_sec: secs as i64, + tv_nsec: nsecs, + }; + secs -= req.tv_sec as u64; + let mut rem = syscall::TimeSpec::default(); + if syscall::nanosleep(&req, &mut rem).is_err() { + secs += rem.tv_sec as u64; + nsecs = rem.tv_nsec; + } else { + nsecs = 0; + } + } + } + + pub fn join(self) { + let mut status = 0; + syscall::waitpid(self.id, &mut status, 0).unwrap(); + } + + pub fn id(&self) -> usize { self.id } + + pub fn into_id(self) -> usize { + let id = self.id; + mem::forget(self); + id + } +} + +pub mod guard { + pub unsafe fn current() -> Option<usize> { None } + pub unsafe fn init() -> Option<usize> { None } +} diff --git a/src/libstd/sys/redox/thread_local.rs b/src/libstd/sys/redox/thread_local.rs new file mode 100644 index 00000000000..abdd9ace795 --- /dev/null +++ b/src/libstd/sys/redox/thread_local.rs @@ -0,0 +1,66 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] // not used on all platforms + +use collections::BTreeMap; +use ptr; +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +pub type Key = usize; + +type Dtor = unsafe extern fn(*mut u8); + +static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT; + +static mut KEYS: *mut BTreeMap<Key, Option<Dtor>> = ptr::null_mut(); + +#[thread_local] +static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut(); + +unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> { + if KEYS == ptr::null_mut() { + KEYS = Box::into_raw(Box::new(BTreeMap::new())); + } + &mut *KEYS +} + +unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> { + if LOCALS == ptr::null_mut() { + LOCALS = Box::into_raw(Box::new(BTreeMap::new())); + } + &mut *LOCALS +} + +#[inline] +pub unsafe fn create(dtor: Option<Dtor>) -> Key { + let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); + keys().insert(key, dtor); + key +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + if let Some(&entry) = locals().get(&key) { + entry + } else { + ptr::null_mut() + } +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + locals().insert(key, value); +} + +#[inline] +pub unsafe fn destroy(key: Key) { + keys().remove(&key); +} diff --git a/src/libstd/sys/redox/time.rs b/src/libstd/sys/redox/time.rs new file mode 100644 index 00000000000..dea406efe6c --- /dev/null +++ b/src/libstd/sys/redox/time.rs @@ -0,0 +1,198 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cmp::Ordering; +use fmt; +use sys::{cvt, syscall}; +use time::Duration; + +const NSEC_PER_SEC: u64 = 1_000_000_000; + +#[derive(Copy, Clone)] +struct Timespec { + t: syscall::TimeSpec, +} + +impl Timespec { + fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { + if self >= other { + Ok(if self.t.tv_nsec >= other.t.tv_nsec { + Duration::new((self.t.tv_sec - other.t.tv_sec) as u64, + (self.t.tv_nsec - other.t.tv_nsec) as u32) + } else { + Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64, + self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - + other.t.tv_nsec as u32) + }) + } else { + match other.sub_timespec(self) { + Ok(d) => Err(d), + Err(d) => Ok(d), + } + } + } + + fn add_duration(&self, other: &Duration) -> Timespec { + let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64); + let mut secs = secs.expect("overflow when adding duration to time"); + + // Nano calculations can't overflow because nanos are <1B which fit + // in a u32. + let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; + if nsec >= NSEC_PER_SEC as u32 { + nsec -= NSEC_PER_SEC as u32; + secs = secs.checked_add(1).expect("overflow when adding \ + duration to time"); + } + Timespec { + t: syscall::TimeSpec { + tv_sec: secs as i64, + tv_nsec: nsec as i32, + }, + } + } + + fn sub_duration(&self, other: &Duration) -> Timespec { + let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64); + let mut secs = secs.expect("overflow when subtracting duration \ + from time"); + + // Similar to above, nanos can't overflow. + let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; + if nsec < 0 { + nsec += NSEC_PER_SEC as i32; + secs = secs.checked_sub(1).expect("overflow when subtracting \ + duration from time"); + } + Timespec { + t: syscall::TimeSpec { + tv_sec: secs as i64, + tv_nsec: nsec as i32, + }, + } + } +} + +impl PartialEq for Timespec { + fn eq(&self, other: &Timespec) -> bool { + self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec + } +} + +impl Eq for Timespec {} + +impl PartialOrd for Timespec { + fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for Timespec { + fn cmp(&self, other: &Timespec) -> Ordering { + let me = (self.t.tv_sec, self.t.tv_nsec); + let other = (other.t.tv_sec, other.t.tv_nsec); + me.cmp(&other) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Instant { + t: Timespec, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct SystemTime { + t: Timespec, +} + +pub const UNIX_EPOCH: SystemTime = SystemTime { + t: Timespec { + t: syscall::TimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + }, +}; + +impl Instant { + pub fn now() -> Instant { + Instant { t: now(syscall::CLOCK_MONOTONIC) } + } + + pub fn sub_instant(&self, other: &Instant) -> Duration { + self.t.sub_timespec(&other.t).unwrap_or_else(|_| { + panic!("other was less than the current instant") + }) + } + + pub fn add_duration(&self, other: &Duration) -> Instant { + Instant { t: self.t.add_duration(other) } + } + + pub fn sub_duration(&self, other: &Duration) -> Instant { + Instant { t: self.t.sub_duration(other) } + } +} + +impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Instant") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + SystemTime { t: now(syscall::CLOCK_REALTIME) } + } + + pub fn sub_time(&self, other: &SystemTime) + -> Result<Duration, Duration> { + self.t.sub_timespec(&other.t) + } + + pub fn add_duration(&self, other: &Duration) -> SystemTime { + SystemTime { t: self.t.add_duration(other) } + } + + pub fn sub_duration(&self, other: &Duration) -> SystemTime { + SystemTime { t: self.t.sub_duration(other) } + } +} + +impl From<syscall::TimeSpec> for SystemTime { + fn from(t: syscall::TimeSpec) -> SystemTime { + SystemTime { t: Timespec { t: t } } + } +} + +impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SystemTime") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } +} + +pub type clock_t = usize; + +fn now(clock: clock_t) -> Timespec { + let mut t = Timespec { + t: syscall::TimeSpec { + tv_sec: 0, + tv_nsec: 0, + } + }; + cvt(syscall::clock_gettime(clock, &mut t.t)).unwrap(); + t +} diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index fcfab051588..900f463fa83 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -21,7 +21,7 @@ use sys_common::{FromInner, AsInner, AsInnerMut}; use sys::platform::fs::MetadataExt as UnixMetadataExt; /// Unix-specific extensions to `File` -#[unstable(feature = "file_offset", issue = "35918")] +#[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { /// Reads a number of bytes starting from a given offset. /// @@ -34,7 +34,7 @@ pub trait FileExt { /// /// Note that similar to `File::read`, it is not an error to return with a /// short read. - #[unstable(feature = "file_offset", issue = "35918")] + #[stable(feature = "file_offset", since = "1.15.0")] fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; /// Writes a number of bytes starting from a given offset. @@ -51,11 +51,11 @@ pub trait FileExt { /// /// Note that similar to `File::write`, it is not an error to return a /// short write. - #[unstable(feature = "file_offset", issue = "35918")] + #[stable(feature = "file_offset", since = "1.15.0")] fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>; } -#[unstable(feature = "file_offset", issue = "35918")] +#[stable(feature = "file_offset", since = "1.15.0")] impl FileExt for fs::File { fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { self.as_inner().read_at(buf, offset) diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs index b2483f4e209..1be9f11b92c 100644 --- a/src/libstd/sys/unix/ext/mod.rs +++ b/src/libstd/sys/unix/ext/mod.rs @@ -50,7 +50,7 @@ pub mod prelude { pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::fs::DirEntryExt; - #[doc(no_inline)] #[unstable(feature = "file_offset", issue = "35918")] + #[doc(no_inline)] #[stable(feature = "file_offset", since = "1.15.0")] pub use super::fs::FileExt; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::thread::JoinHandleExt; diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 80f53da1cef..1ba4a104e51 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -85,6 +85,21 @@ enum AddressKind<'a> { } /// An address associated with a Unix socket. +/// +/// # Examples +/// +/// ``` +/// use std::os::unix::net::UnixListener; +/// +/// let socket = match UnixListener::bind("/tmp/sock") { +/// Ok(sock) => sock, +/// Err(e) => { +/// println!("Couldn't bind: {:?}", e); +/// return +/// } +/// }; +/// let addr = socket.local_addr().expect("Couldn't get local address"); +/// ``` #[derive(Clone)] #[stable(feature = "unix_socket", since = "1.10.0")] pub struct SocketAddr { @@ -121,6 +136,28 @@ impl SocketAddr { } /// Returns true if and only if the address is unnamed. + /// + /// # Examples + /// + /// A named address: + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let socket = UnixListener::bind("/tmp/sock").unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), false); + /// ``` + /// + /// An unnamed address: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound().unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), true); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn is_unnamed(&self) -> bool { if let AddressKind::Unnamed = self.address() { @@ -131,6 +168,29 @@ impl SocketAddr { } /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// use std::path::Path; + /// + /// let socket = UnixListener::bind("/tmp/sock").unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); + /// ``` + /// + /// Without a pathname: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound().unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), None); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn as_pathname(&self) -> Option<&Path> { if let AddressKind::Pathname(path) = self.address() { @@ -182,7 +242,7 @@ impl<'a> fmt::Display for AsciiEscaped<'a> { /// /// # Examples /// -/// ```rust,no_run +/// ```no_run /// use std::os::unix::net::UnixStream; /// use std::io::prelude::*; /// @@ -212,6 +272,20 @@ impl fmt::Debug for UnixStream { impl UnixStream { /// Connects to the socket named by `path`. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = match UnixStream::connect("/tmp/sock") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return + /// } + /// }; + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> { fn inner(path: &Path) -> io::Result<UnixStream> { @@ -229,6 +303,20 @@ impl UnixStream { /// Creates an unnamed pair of connected sockets. /// /// Returns two `UnixStream`s which are connected to each other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let (sock1, sock2) = match UnixStream::pair() { + /// Ok((sock1, sock2)) => (sock1, sock2), + /// Err(e) => { + /// println!("Couldn't create a pair of sockets: {:?}", e); + /// return + /// } + /// }; + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn pair() -> io::Result<(UnixStream, UnixStream)> { let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_STREAM)?; @@ -241,18 +329,45 @@ impl UnixStream { /// 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 /// stream. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn try_clone(&self) -> io::Result<UnixStream> { self.0.duplicate().map(UnixStream) } /// Returns the socket address of the local half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) } /// Returns the socket address of the remote half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) @@ -260,9 +375,23 @@ impl UnixStream { /// Sets the read timeout for the socket. /// - /// If the provided value is `None`, then `read` calls will block - /// indefinitely. It is an error to pass the zero `Duration` to this + /// If the provided value is [`None`], then [`read()`] calls will block + /// indefinitely. It is an error to pass the zero [`Duration`] to this /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`read()`]: ../../../../std/io/trait.Read.html#tymethod.read + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_RCVTIMEO) @@ -270,33 +399,89 @@ impl UnixStream { /// Sets the write timeout for the socket. /// - /// If the provided value is `None`, then `write` calls will block - /// indefinitely. It is an error to pass the zero `Duration` to this + /// If the provided value is [`None`], then [`write()`] calls will block + /// indefinitely. It is an error to pass the zero [`Duration`] to this /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`read()`]: ../../../../std/io/trait.Write.html#tymethod.write + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_SNDTIMEO) } /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// assert_eq!(socket.read_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn read_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_RCVTIMEO) } /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout"); + /// assert_eq!(socket.write_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn write_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_SNDTIMEO) } /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// if let Ok(Some(err)) = socket.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() @@ -306,7 +491,19 @@ impl UnixStream { /// /// This function will cause all pending and future I/O calls on the /// specified portions to immediately return with an appropriate value - /// (see the documentation of `Shutdown`). + /// (see the documentation of [`Shutdown`]). + /// + /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::net::Shutdown; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { self.0.shutdown(how) @@ -382,7 +579,7 @@ impl IntoRawFd for UnixStream { /// /// # Examples /// -/// ```rust,no_run +/// ```no_run /// use std::thread; /// use std::os::unix::net::{UnixStream, UnixListener}; /// @@ -405,9 +602,6 @@ impl IntoRawFd for UnixStream { /// } /// } /// } -/// -/// // close the listener socket -/// drop(listener); /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub struct UnixListener(Socket); @@ -426,6 +620,20 @@ impl fmt::Debug for UnixListener { impl UnixListener { /// Creates a new `UnixListener` bound to the specified socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = match UnixListener::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return + /// } + /// }; + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> { fn inner(path: &Path) -> io::Result<UnixListener> { @@ -445,8 +653,23 @@ impl UnixListener { /// Accepts a new incoming connection to this listener. /// /// This function will block the calling thread until a new Unix connection - /// is established. When established, the corersponding `UnixStream` and + /// is established. When established, the corersponding [`UnixStream`] and /// the remote peer's address will be returned. + /// + /// [`UnixStream`]: ../../../../std/os/unix/net/struct.UnixStream.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// match listener.accept() { + /// Ok((socket, addr)) => println!("Got a client: {:?}", addr), + /// Err(e) => println!("accept function failed: {:?}", e), + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() }; @@ -461,24 +684,66 @@ impl UnixListener { /// The returned `UnixListener` is a reference to the same socket that this /// object references. Both handles can be used to accept incoming /// connections and options set on one listener will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn try_clone(&self) -> io::Result<UnixListener> { self.0.duplicate().map(UnixListener) } /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) } /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/tmp/sock").unwrap(); + /// + /// if let Ok(Some(err)) = listener.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() @@ -486,8 +751,35 @@ impl UnixListener { /// Returns an iterator over incoming connections. /// - /// The iterator will never return `None` and will also not yield the - /// peer's `SocketAddr` structure. + /// The iterator will never return [`None`] and will also not yield the + /// peer's [`SocketAddr`] structure. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`SocketAddr`]: struct.SocketAddr.html + /// + /// # Examples + /// + /// ```no_run + /// use std::thread; + /// use std::os::unix::net::{UnixStream, UnixListener}; + /// + /// fn handle_client(stream: UnixStream) { + /// // ... + /// } + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// thread::spawn(|| handle_client(stream)); + /// } + /// Err(err) => { + /// break; + /// } + /// } + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn incoming<'a>(&'a self) -> Incoming<'a> { Incoming { listener: self } @@ -525,9 +817,36 @@ impl<'a> IntoIterator for &'a UnixListener { } } -/// An iterator over incoming connections to a `UnixListener`. +/// An iterator over incoming connections to a [`UnixListener`]. +/// +/// It will never return [`None`]. +/// +/// [`None`]: ../../../../std/option/enum.Option.html#variant.None +/// [`UnixListener`]: struct.UnixListener.html +/// +/// # Examples /// -/// It will never return `None`. +/// ```no_run +/// use std::thread; +/// use std::os::unix::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); +/// +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// break; +/// } +/// } +/// } +/// ``` #[derive(Debug)] #[stable(feature = "unix_socket", since = "1.10.0")] pub struct Incoming<'a> { @@ -551,7 +870,7 @@ impl<'a> Iterator for Incoming<'a> { /// /// # Examples /// -/// ```rust,no_run +/// ```no_run /// use std::os::unix::net::UnixDatagram; /// /// let socket = UnixDatagram::bind("/path/to/my/socket").unwrap(); @@ -580,6 +899,20 @@ impl fmt::Debug for UnixDatagram { impl UnixDatagram { /// Creates a Unix datagram socket bound to the given path. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = match UnixDatagram::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't bind: {:?}", e); + /// return + /// } + /// }; + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> { fn inner(path: &Path) -> io::Result<UnixDatagram> { @@ -596,6 +929,20 @@ impl UnixDatagram { } /// Creates a Unix Datagram socket which is not bound to any address. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = match UnixDatagram::unbound() { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't unbound: {:?}", e); + /// return + /// } + /// }; + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn unbound() -> io::Result<UnixDatagram> { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_DGRAM)?; @@ -605,6 +952,20 @@ impl UnixDatagram { /// Create an unnamed pair of connected sockets. /// /// Returns two `UnixDatagrams`s which are connected to each other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let (sock1, sock2) = match UnixDatagram::pair() { + /// Ok((sock1, sock2)) => (sock1, sock2), + /// Err(e) => { + /// println!("Couldn't unbound: {:?}", e); + /// return + /// } + /// }; + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_DGRAM)?; @@ -613,8 +974,27 @@ impl UnixDatagram { /// Connects the socket to the specified address. /// - /// The `send` method may be used to send data to the specified address. - /// `recv` and `recv_from` will only receive data from that address. + /// The [`send()`] method may be used to send data to the specified address. + /// [`recv()`] and [`recv_from()`] will only receive data from that address. + /// + /// [`send()`]: #method.send + /// [`recv()`]: #method.recv + /// [`recv_from()`]: #method.recv_from + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// match sock.connect("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return + /// } + /// }; + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { fn inner(d: &UnixDatagram, path: &Path) -> io::Result<()> { @@ -631,15 +1011,35 @@ impl UnixDatagram { /// Creates a new independently owned handle to the underlying socket. /// - /// The returned `UnixListener` is a reference to the same socket that this + /// The returned `UnixDatagram` is a reference to the same socket that this /// object references. Both handles can be used to accept incoming - /// connections and options set on one listener will affect the other. + /// connections and options set on one side will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap(); + /// + /// let sock_copy = sock.try_clone().expect("try_clone failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn try_clone(&self) -> io::Result<UnixDatagram> { self.0.duplicate().map(UnixDatagram) } /// Returns the address of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap(); + /// + /// let addr = sock.local_addr().expect("Couldn't get local address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) @@ -647,7 +1047,20 @@ impl UnixDatagram { /// Returns the address of this socket's peer. /// - /// The `connect` method will connect the socket to a peer. + /// The [`connect()`] method will connect the socket to a peer. + /// + /// [`connect()`]: #method.connect + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.connect("/path/to/the/socket").unwrap(); + /// + /// let addr = sock.peer_addr().expect("Couldn't get peer address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) @@ -657,6 +1070,19 @@ impl UnixDatagram { /// /// On success, returns the number of bytes read and the address from /// whence the data came. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// let mut buf = vec![0; 10]; + /// match sock.recv_from(buf.as_mut_slice()) { + /// Ok((size, sender)) => println!("received {} bytes from {:?}", size, sender), + /// Err(e) => println!("recv_from function failed: {:?}", e), + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { let mut count = 0; @@ -684,6 +1110,16 @@ impl UnixDatagram { /// Receives data from the socket. /// /// On success, returns the number of bytes read. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap(); + /// let mut buf = vec![0; 10]; + /// sock.recv(buf.as_mut_slice()).expect("recv function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) @@ -692,6 +1128,15 @@ impl UnixDatagram { /// Sends data on the socket to the specified address. /// /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> { fn inner(d: &UnixDatagram, buf: &[u8], path: &Path) -> io::Result<usize> { @@ -716,6 +1161,16 @@ impl UnixDatagram { /// will return an error if the socket has not already been connected. /// /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.connect("/some/sock").expect("Couldn't connect"); + /// sock.send(b"omelette au fromage").expect("send_to function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn send(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) @@ -723,9 +1178,24 @@ impl UnixDatagram { /// Sets the read timeout for the socket. /// - /// If the provided value is `None`, then `recv` and `recv_from` calls will - /// block indefinitely. It is an error to pass the zero `Duration` to this + /// If the provided value is [`None`], then [`recv()`] and [`recv_from()`] calls will + /// block indefinitely. It is an error to pass the zero [`Duration`] to this /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`recv()`]: #method.recv + /// [`recv_from()`]: #method.recv_from + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_RCVTIMEO) @@ -733,33 +1203,92 @@ impl UnixDatagram { /// Sets the write timeout for the socket. /// - /// If the provided value is `None`, then `send` and `send_to` calls will - /// block indefinitely. It is an error to pass the zero `Duration` to this + /// If the provided value is [`None`], then [`send()`] and [`send_to()`] calls will + /// block indefinitely. It is an error to pass the zero [`Duration`] to this /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`send()`]: #method.send + /// [`send_to()`]: #method.send_to + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_SNDTIMEO) } /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed"); + /// assert_eq!(sock.read_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn read_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_RCVTIMEO) } /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// assert_eq!(sock.write_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn write_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_SNDTIMEO) } /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_nonblocking(true).expect("set_nonblocking function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// if let Ok(Some(err)) = sock.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() @@ -769,7 +1298,17 @@ impl UnixDatagram { /// /// This function will cause all pending and future I/O calls on the /// specified portions to immediately return with an appropriate value - /// (see the documentation of `Shutdown`). + /// (see the documentation of [`Shutdown`]). + /// + /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// use std::net::Shutdown; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { self.0.shutdown(how) diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index 3a7c59d4e6d..585dcbb9a34 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -56,7 +56,7 @@ pub trait CommandExt { /// When this closure is run, aspects such as the stdio file descriptors and /// working directory have successfully been changed, so output to these /// locations may not appear where intended. - #[unstable(feature = "process_exec", issue = "31398")] + #[stable(feature = "process_exec", since = "1.15.0")] fn before_exec<F>(&mut self, f: F) -> &mut process::Command where F: FnMut() -> io::Result<()> + Send + Sync + 'static; diff --git a/src/libstd/sys/unix/fast_thread_local.rs b/src/libstd/sys/unix/fast_thread_local.rs index 0c625e7add9..f4f73646e1b 100644 --- a/src/libstd/sys/unix/fast_thread_local.rs +++ b/src/libstd/sys/unix/fast_thread_local.rs @@ -12,6 +12,7 @@ #![unstable(feature = "thread_local_internals", issue = "0")] use cell::{Cell, UnsafeCell}; +use fmt; use intrinsics; use ptr; @@ -24,6 +25,12 @@ pub struct Key<T> { dtor_running: Cell<bool>, } +impl<T> fmt::Debug for Key<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Key { .. }") + } +} + unsafe impl<T> ::marker::Sync for Key<T> { } impl<T> Key<T> { diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 61eb60da486..2384d959881 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -18,6 +18,7 @@ use sys::cvt; use sys_common::AsInner; use sys_common::io::read_to_end_uninitialized; +#[derive(Debug)] pub struct FileDesc { fd: c_int, } diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 9ee0458b5da..8b5c0c04276 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -66,7 +66,7 @@ pub struct DirEntry { name: Box<[u8]> } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct OpenOptions { // generic read: bool, @@ -86,6 +86,7 @@ pub struct FilePermissions { mode: mode_t } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct FileType { mode: mode_t } +#[derive(Debug)] pub struct DirBuilder { mode: mode_t } impl FileAttr { diff --git a/src/libstd/sys/unix/process/magenta.rs b/src/libstd/sys/unix/process/magenta.rs index 319fbce35cd..2bb005be4ec 100644 --- a/src/libstd/sys/unix/process/magenta.rs +++ b/src/libstd/sys/unix/process/magenta.rs @@ -23,7 +23,6 @@ pub type mx_rights_t = u32; pub type mx_status_t = i32; pub type mx_size_t = usize; -pub type mx_ssize_t = isize; pub const MX_HANDLE_INVALID: mx_handle_t = 0; diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index 273341b1918..6d38b00b39e 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -43,6 +43,10 @@ impl Stdout { fd.into_raw(); ret } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } } impl Stderr { @@ -54,6 +58,10 @@ impl Stderr { fd.into_raw(); ret } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } } // FIXME: right now this raw stderr handle is used in a few places because @@ -63,7 +71,10 @@ impl io::Write for Stderr { fn write(&mut self, data: &[u8]) -> io::Result<usize> { Stderr::write(self, data) } - fn flush(&mut self) -> io::Result<()> { Ok(()) } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } } pub const EBADF_ERR: i32 = ::libc::EBADF as i32; diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 1a563127f7f..d1c404195bc 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -47,7 +47,9 @@ pub type CHAR = c_char; pub type HCRYPTPROV = LONG_PTR; pub type ULONG_PTR = c_ulonglong; pub type ULONG = c_ulong; +#[cfg(target_arch = "x86_64")] pub type ULONGLONG = u64; +#[cfg(target_arch = "x86_64")] pub type DWORDLONG = ULONGLONG; pub type LPBOOL = *mut BOOL; @@ -66,7 +68,6 @@ pub type LPVOID = *mut c_void; pub type LPWCH = *mut WCHAR; pub type LPWIN32_FIND_DATAW = *mut WIN32_FIND_DATAW; pub type LPWSADATA = *mut WSADATA; -pub type LPWSAPROTOCOLCHAIN = *mut WSAPROTOCOLCHAIN; pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO; pub type LPWSTR = *mut WCHAR; pub type LPFILETIME = *mut FILETIME; @@ -311,8 +312,6 @@ pub struct WSADATA { pub szSystemStatus: [u8; WSASYS_STATUS_LEN + 1], } -pub type WSAEVENT = HANDLE; - #[repr(C)] pub struct WSAPROTOCOL_INFO { pub dwServiceFlags1: DWORD, @@ -819,6 +818,16 @@ pub enum EXCEPTION_DISPOSITION { ExceptionCollidedUnwind } +#[repr(C)] +#[derive(Copy, Clone)] +pub struct CONSOLE_READCONSOLE_CONTROL { + pub nLength: ULONG, + pub nInitialChars: ULONG, + pub dwCtrlWakeupMask: ULONG, + pub dwControlKeyState: ULONG, +} +pub type PCONSOLE_READCONSOLE_CONTROL = *mut CONSOLE_READCONSOLE_CONTROL; + #[link(name = "ws2_32")] #[link(name = "userenv")] #[link(name = "shell32")] @@ -849,12 +858,11 @@ extern "system" { pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION); - // FIXME - pInputControl should be PCONSOLE_READCONSOLE_CONTROL pub fn ReadConsoleW(hConsoleInput: HANDLE, lpBuffer: LPVOID, nNumberOfCharsToRead: DWORD, lpNumberOfCharsRead: LPDWORD, - pInputControl: LPVOID) -> BOOL; + pInputControl: PCONSOLE_READCONSOLE_CONTROL) -> BOOL; pub fn WriteConsoleW(hConsoleOutput: HANDLE, lpBuffer: LPCVOID, diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index 1e2b8bf38fa..7fc04ad69d6 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -19,7 +19,7 @@ use sys; use sys_common::{AsInnerMut, AsInner}; /// Windows-specific extensions to `File` -#[unstable(feature = "file_offset", issue = "35918")] +#[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { /// Seeks to a given position and reads a number of bytes. /// @@ -35,7 +35,7 @@ pub trait FileExt { /// Note that similar to `File::read`, it is not an error to return with a /// short read. When returning from such a short read, the file pointer is /// still updated. - #[unstable(feature = "file_offset", issue = "35918")] + #[stable(feature = "file_offset", since = "1.15.0")] fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; /// Seeks to a given position and writes a number of bytes. @@ -52,11 +52,11 @@ pub trait FileExt { /// Note that similar to `File::write`, it is not an error to return a /// short write. When returning from such a short write, the file pointer /// is still updated. - #[unstable(feature = "file_offset", issue = "35918")] + #[stable(feature = "file_offset", since = "1.15.0")] fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>; } -#[unstable(feature = "file_offset", issue = "35918")] +#[stable(feature = "file_offset", since = "1.15.0")] impl FileExt for fs::File { fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { self.as_inner().read_at(buf, offset) diff --git a/src/libstd/sys/windows/ext/mod.rs b/src/libstd/sys/windows/ext/mod.rs index 932bb5e9564..f12e50cc923 100644 --- a/src/libstd/sys/windows/ext/mod.rs +++ b/src/libstd/sys/windows/ext/mod.rs @@ -36,6 +36,6 @@ pub mod prelude { pub use super::ffi::{OsStrExt, OsStringExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::fs::{OpenOptionsExt, MetadataExt}; - #[doc(no_inline)] #[unstable(feature = "file_offset", issue = "35918")] + #[doc(no_inline)] #[stable(feature = "file_offset", since = "1.15.0")] pub use super::fs::FileExt; } diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 7d7d78bbd87..c410fcd1ee0 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -58,7 +58,7 @@ pub struct DirEntry { data: c::WIN32_FIND_DATAW, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct OpenOptions { // generic read: bool, @@ -79,6 +79,7 @@ pub struct OpenOptions { #[derive(Clone, PartialEq, Eq, Debug)] pub struct FilePermissions { attrs: c::DWORD } +#[derive(Debug)] pub struct DirBuilder; impl fmt::Debug for ReadDir { diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index 72788776ded..b1a57c349fb 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -111,19 +111,27 @@ impl Stdin { if utf8.position() as usize == utf8.get_ref().len() { let mut utf16 = vec![0u16; 0x1000]; let mut num = 0; + let mut input_control = readconsole_input_control(CTRL_Z_MASK); cvt(unsafe { c::ReadConsoleW(handle, utf16.as_mut_ptr() as c::LPVOID, utf16.len() as u32, &mut num, - ptr::null_mut()) + &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL) })?; utf16.truncate(num as usize); // FIXME: what to do about this data that has already been read? - let data = match String::from_utf16(&utf16) { + let mut data = match String::from_utf16(&utf16) { Ok(utf8) => utf8.into_bytes(), Err(..) => return Err(invalid_encoding()), }; + if let Output::Console(_) = self.handle { + if let Some(&last_byte) = data.last() { + if last_byte == CTRL_Z { + data.pop(); + } + } + } *utf8 = Cursor::new(data); } @@ -156,6 +164,10 @@ impl Stdout { pub fn write(&self, data: &[u8]) -> io::Result<usize> { write(&self.0, data) } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } } impl Stderr { @@ -166,6 +178,10 @@ impl Stderr { pub fn write(&self, data: &[u8]) -> io::Result<usize> { write(&self.0, data) } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } } // FIXME: right now this raw stderr handle is used in a few places because @@ -175,7 +191,10 @@ impl io::Write for Stderr { fn write(&mut self, data: &[u8]) -> io::Result<usize> { Stderr::write(self, data) } - fn flush(&mut self) -> io::Result<()> { Ok(()) } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } } impl NoClose { @@ -206,6 +225,18 @@ fn invalid_encoding() -> io::Error { io::Error::new(io::ErrorKind::InvalidData, "text was not valid unicode") } +fn readconsole_input_control(wakeup_mask: c::ULONG) -> c::CONSOLE_READCONSOLE_CONTROL { + c::CONSOLE_READCONSOLE_CONTROL { + nLength: ::mem::size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as c::ULONG, + nInitialChars: 0, + dwCtrlWakeupMask: wakeup_mask, + dwControlKeyState: 0, + } +} + +const CTRL_Z: u8 = 0x1A; +const CTRL_Z_MASK: c::ULONG = 0x4000000; //1 << 0x1A + pub const EBADF_ERR: i32 = ::sys::c::ERROR_INVALID_HANDLE as i32; // The default buffer capacity is 64k, but apparently windows // doesn't like 64k reads on stdin. See #13304 for details, but the diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index fe17d8cb5e5..634d6258885 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -23,6 +23,7 @@ //! `std::sys` from the standard library. #![allow(missing_docs)] +#![allow(missing_debug_implementations)] use sync::Once; use sys; @@ -34,7 +35,6 @@ pub mod condvar; pub mod io; pub mod memchr; pub mod mutex; -pub mod net; pub mod poison; pub mod remutex; pub mod rwlock; @@ -44,6 +44,12 @@ pub mod thread_local; pub mod util; pub mod wtf8; +#[cfg(target_os = "redox")] +pub use sys::net; + +#[cfg(not(target_os = "redox"))] +pub mod net; + #[cfg(any(not(cargobuild), feature = "backtrace"))] #[cfg(any(all(unix, not(any(target_os = "macos", target_os = "ios", target_os = "emscripten"))), all(windows, target_env = "gnu")))] diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index f74dd592495..01584979aab 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -13,6 +13,7 @@ #![unstable(feature = "thread_local_internals", issue = "0")] use cell::UnsafeCell; +use fmt; use mem; /// A thread local storage key which owns its contents. @@ -98,6 +99,13 @@ pub struct LocalKey<T: 'static> { init: fn() -> T, } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<T: 'static> fmt::Debug for LocalKey<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("LocalKey { .. }") + } +} + /// Declare a new thread local storage key of type `std::thread::LocalKey`. /// /// # Syntax @@ -184,7 +192,7 @@ macro_rules! __thread_local_inner { #[unstable(feature = "thread_local_state", reason = "state querying was recently added", issue = "27716")] -#[derive(Eq, PartialEq, Copy, Clone)] +#[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum LocalKeyState { /// All keys are in this state whenever a thread starts. Keys will /// transition to the `Valid` state once the first call to `with` happens @@ -313,6 +321,7 @@ impl<T: 'static> LocalKey<T> { #[doc(hidden)] pub mod os { use cell::{Cell, UnsafeCell}; + use fmt; use marker; use ptr; use sys_common::thread_local::StaticKey as OsStaticKey; @@ -323,6 +332,13 @@ pub mod os { marker: marker::PhantomData<Cell<T>>, } + #[stable(feature = "std_debug", since = "1.15.0")] + impl<T> fmt::Debug for Key<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Key { .. }") + } + } + unsafe impl<T> ::marker::Sync for Key<T> { } struct Value<T: 'static> { diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 255cd2a9bc0..6d8b3cc93d9 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -17,13 +17,11 @@ //! provide some built-in support for low-level synchronization. //! //! Communication between threads can be done through -//! [channels](../../std/sync/mpsc/index.html), Rust's message-passing -//! types, along with [other forms of thread +//! [channels], Rust's message-passing types, along with [other forms of thread //! synchronization](../../std/sync/index.html) and shared-memory data //! structures. In particular, types that are guaranteed to be //! threadsafe are easily shared between threads using the -//! atomically-reference-counted container, -//! [`Arc`](../../std/sync/struct.Arc.html). +//! atomically-reference-counted container, [`Arc`]. //! //! Fatal logic errors in Rust cause *thread panic*, during which //! a thread will unwind the stack, running destructors and freeing @@ -40,7 +38,7 @@ //! //! ## Spawning a thread //! -//! A new thread can be spawned using the `thread::spawn` function: +//! A new thread can be spawned using the [`thread::spawn`][`spawn`] function: //! //! ```rust //! use std::thread; @@ -55,7 +53,7 @@ //! it), unless this parent is the main thread. //! //! The parent thread can also wait on the completion of the child -//! thread; a call to `spawn` produces a `JoinHandle`, which provides +//! thread; a call to [`spawn`] produces a [`JoinHandle`], which provides //! a `join` method for waiting: //! //! ```rust @@ -68,13 +66,13 @@ //! let res = child.join(); //! ``` //! -//! The `join` method returns a `Result` containing `Ok` of the final -//! value produced by the child thread, or `Err` of the value given to -//! a call to `panic!` if the child panicked. +//! The [`join`] method returns a [`Result`] containing [`Ok`] of the final +//! value produced by the child thread, or [`Err`] of the value given to +//! a call to [`panic!`] if the child panicked. //! //! ## Configuring threads //! -//! A new thread can be configured before it is spawned via the `Builder` type, +//! A new thread can be configured before it is spawned via the [`Builder`] type, //! which currently allows you to set the name and stack size for the child thread: //! //! ```rust @@ -88,43 +86,43 @@ //! //! ## The `Thread` type //! -//! Threads are represented via the `Thread` type, which you can get in one of +//! Threads are represented via the [`Thread`] type, which you can get in one of //! two ways: //! -//! * By spawning a new thread, e.g. using the `thread::spawn` function, and -//! calling `thread()` on the `JoinHandle`. -//! * By requesting the current thread, using the `thread::current` function. +//! * By spawning a new thread, e.g. using the [`thread::spawn`][`spawn`] +//! function, and calling [`thread()`] on the [`JoinHandle`]. +//! * By requesting the current thread, using the [`thread::current()`] function. //! -//! The `thread::current()` function is available even for threads not spawned +//! The [`thread::current()`] function is available even for threads not spawned //! by the APIs of this module. //! //! ## Blocking support: park and unpark //! //! Every thread is equipped with some basic low-level blocking support, via the -//! `thread::park()` function and `thread::Thread::unpark()` method. `park()` -//! blocks the current thread, which can then be resumed from another thread by -//! calling the `unpark()` method on the blocked thread's handle. +//! [`thread::park()`][`park()`] function and [`thread::Thread::unpark()`][`unpark()`] +//! method. [`park()`] blocks the current thread, which can then be resumed from +//! another thread by calling the [`unpark()`] method on the blocked thread's handle. //! -//! Conceptually, each `Thread` handle has an associated token, which is +//! Conceptually, each [`Thread`] handle has an associated token, which is //! initially not present: //! -//! * The `thread::park()` function blocks the current thread unless or until +//! * The [`thread::park()`][`park()`] function blocks the current thread unless or until //! the token is available for its thread handle, at which point it atomically //! consumes the token. It may also return *spuriously*, without consuming the -//! token. `thread::park_timeout()` does the same, but allows specifying a +//! token. [`thread::park_timeout()`] does the same, but allows specifying a //! maximum time to block the thread for. //! -//! * The `unpark()` method on a `Thread` atomically makes the token available +//! * The [`unpark()`] method on a [`Thread`] atomically makes the token available //! if it wasn't already. //! -//! In other words, each `Thread` acts a bit like a semaphore with initial count +//! In other words, each [`Thread`] acts a bit like a semaphore with initial count //! 0, except that the semaphore is *saturating* (the count cannot go above 1), //! and can return spuriously. //! //! The API is typically used by acquiring a handle to the current thread, //! placing that handle in a shared data structure so that other threads can //! find it, and then `park`ing. When some desired condition is met, another -//! thread calls `unpark` on the handle. +//! thread calls [`unpark()`] on the handle. //! //! The motivation for this design is twofold: //! @@ -149,6 +147,22 @@ //! will want to make use of some form of **interior mutability** through the //! [`Cell`] or [`RefCell`] types. //! +//! [channels]: ../../std/sync/mpsc/index.html +//! [`Arc`]: ../../std/sync/struct.Arc.html +//! [`spawn`]: ../../std/thread/fn.spawn.html +//! [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html +//! [`thread()`]: ../../std/thread/struct.JoinHandle.html#method.thread +//! [`join`]: ../../std/thread/struct.JoinHandle.html#method.join +//! [`Result`]: ../../std/result/enum.Result.html +//! [`Ok`]: ../../std/result/enum.Result.html#variant.Ok +//! [`Err`]: ../../std/result/enum.Result.html#variant.Err +//! [`panic!`]: ../../std/macro.panic.html +//! [`Builder`]: ../../std/thread/struct.Builder.html +//! [`thread::current()`]: ../../std/thread/fn.spawn.html +//! [`Thread`]: ../../std/thread/struct.Thread.html +//! [`park()`]: ../../std/thread/fn.park.html +//! [`unpark()`]: ../../std/thread/struct.Thread.html#method.unpark +//! [`thread::park_timeout()`]: ../../std/thread/fn.park_timeout.html //! [`Cell`]: ../cell/struct.Cell.html //! [`RefCell`]: ../cell/struct.RefCell.html //! [`thread_local!`]: ../macro.thread_local.html @@ -203,6 +217,7 @@ pub use self::local::{LocalKey, LocalKeyState}; /// Thread configuration. Provides detailed control over the properties /// and behavior of new threads. #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct Builder { // A name for the thread-to-be, for identification in panic messages name: Option<String>, @@ -303,20 +318,38 @@ impl Builder { // Free functions //////////////////////////////////////////////////////////////////////////////// -/// Spawns a new thread, returning a `JoinHandle` for it. +/// Spawns a new thread, returning a [`JoinHandle`] for it. /// /// The join handle will implicitly *detach* the child thread upon being /// dropped. In this case, the child thread may outlive the parent (unless /// the parent thread is the main thread; the whole process is terminated when -/// the main thread finishes.) Additionally, the join handle provides a `join` +/// the main thread finishes). Additionally, the join handle provides a [`join`] /// method that can be used to join the child thread. If the child thread -/// panics, `join` will return an `Err` containing the argument given to -/// `panic`. +/// panics, [`join`] will return an [`Err`] containing the argument given to +/// [`panic`]. /// /// # Panics /// -/// Panics if the OS fails to create a thread; use `Builder::spawn` +/// Panics if the OS fails to create a thread; use [`Builder::spawn`] /// to recover from such errors. +/// +/// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html +/// [`join`]: ../../std/thread/struct.JoinHandle.html#method.join +/// [`Err`]: ../../std/result/enum.Result.html#variant.Err +/// [`panic!`]: ../../std/macro.panic.html +/// [`Builder::spawn`]: ../../std/thread/struct.Builder.html#method.spawn +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::spawn(|| { +/// // thread code +/// }); +/// +/// handler.join().unwrap(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn spawn<F, T>(f: F) -> JoinHandle<T> where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static @@ -326,7 +359,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T> where /// Gets a handle to the thread that invokes it. /// -/// #Examples +/// # Examples /// /// Getting a handle to the current thread with `thread::current()`: /// @@ -351,6 +384,14 @@ pub fn current() -> Thread { } /// Cooperatively gives up a timeslice to the OS scheduler. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// thread::yield_now(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn yield_now() { imp::Thread::yield_now() @@ -360,7 +401,7 @@ pub fn yield_now() { /// /// # Examples /// -/// ```rust,should_panic +/// ```should_panic /// use std::thread; /// /// struct SomeStruct; @@ -398,6 +439,15 @@ pub fn panicking() -> bool { /// specifics or platform-dependent functionality. Note that on unix platforms /// this function will not return early due to a signal being received or a /// spurious wakeup. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// +/// // Let's sleep for 2 seconds: +/// thread::sleep_ms(2000); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::thread::sleep`")] pub fn sleep_ms(ms: u32) { @@ -418,7 +468,7 @@ pub fn sleep_ms(ms: u32) { /// /// # Examples /// -/// ```rust,no_run +/// ```no_run /// use std::{thread, time}; /// /// let ten_millis = time::Duration::from_millis(10); @@ -573,6 +623,13 @@ impl ThreadId { } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl fmt::Debug for ThreadId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("ThreadId { .. }") + } +} + //////////////////////////////////////////////////////////////////////////////// // Thread //////////////////////////////////////////////////////////////////////////////// @@ -727,7 +784,7 @@ impl<T> JoinInner<T> { /// /// A `JoinHandle` *detaches* the child thread when it is dropped. /// -/// Due to platform restrictions, it is not possible to `Clone` this +/// Due to platform restrictions, it is not possible to [`Clone`] this /// handle: the ability to join a child thread is a uniquely-owned /// permission. /// @@ -738,7 +795,7 @@ impl<T> JoinInner<T> { /// /// Creation from [`thread::spawn`]: /// -/// ```rust +/// ``` /// use std::thread; /// /// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { @@ -748,7 +805,7 @@ impl<T> JoinInner<T> { /// /// Creation from [`thread::Builder::spawn`]: /// -/// ```rust +/// ``` /// use std::thread; /// /// let builder = thread::Builder::new(); @@ -758,13 +815,31 @@ impl<T> JoinInner<T> { /// }).unwrap(); /// ``` /// +/// [`Clone`]: ../../std/clone/trait.Clone.html /// [`thread::spawn`]: fn.spawn.html /// [`thread::Builder::spawn`]: struct.Builder.html#method.spawn #[stable(feature = "rust1", since = "1.0.0")] pub struct JoinHandle<T>(JoinInner<T>); impl<T> JoinHandle<T> { - /// Extracts a handle to the underlying thread + /// Extracts a handle to the underlying thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(thread_id)] + /// + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// + /// let thread = join_handle.thread(); + /// println!("thread id: {:?}", thread.id()); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn thread(&self) -> &Thread { &self.0.thread @@ -772,8 +847,24 @@ impl<T> JoinHandle<T> { /// Waits for the associated thread to finish. /// - /// If the child thread panics, `Err` is returned with the parameter given - /// to `panic`. + /// If the child thread panics, [`Err`] is returned with the parameter given + /// to [`panic`]. + /// + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// [`panic!`]: ../../std/macro.panic.html + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// join_handle.join().expect("Couldn't join on the associated thread"); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn join(mut self) -> Result<T> { self.0.join() @@ -788,6 +879,13 @@ impl<T> IntoInner<imp::Thread> for JoinHandle<T> { fn into_inner(self) -> imp::Thread { self.0.native.unwrap() } } +#[stable(feature = "std_debug", since = "1.15.0")] +impl<T> fmt::Debug for JoinHandle<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("JoinHandle { .. }") + } +} + fn _assert_sync_and_send() { fn _assert_both<T: Send + Sync>() {} _assert_both::<JoinHandle<()>>(); diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 41d675b6f88..162ce530f17 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -14,16 +14,19 @@ const NANOS_PER_SEC: u32 = 1_000_000_000; const NANOS_PER_MILLI: u32 = 1_000_000; const MILLIS_PER_SEC: u64 = 1_000; -/// A duration type to represent a span of time, typically used for system +/// A `Duration` type to represent a span of time, typically used for system /// timeouts. /// -/// Each duration is composed of a number of seconds and nanosecond precision. +/// Each `Duration` is composed of a number of seconds and nanosecond precision. /// APIs binding a system timeout will typically round up the nanosecond /// precision if the underlying system does not support that level of precision. /// -/// Durations implement many common traits, including `Add`, `Sub`, and other -/// ops traits. Currently a duration may only be inspected for its number of -/// seconds and its nanosecond precision. +/// `Duration`s implement many common traits, including [`Add`], [`Sub`], and other +/// [`ops`] traits. +/// +/// [`Add`]: ../../std/ops/trait.Add.html +/// [`Sub`]: ../../std/ops/trait.Sub.html +/// [`ops`]: ../../std/ops/index.html /// /// # Examples /// @@ -56,6 +59,14 @@ impl Duration { /// /// This constructor will panic if the carry from the nanoseconds overflows /// the seconds counter. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let five_seconds = Duration::new(5, 0); + /// ``` #[stable(feature = "duration", since = "1.3.0")] #[inline] pub fn new(secs: u64, nanos: u32) -> Duration { @@ -66,6 +77,14 @@ impl Duration { } /// Creates a new `Duration` from the specified number of seconds. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let five_seconds = Duration::from_secs(5); + /// ``` #[stable(feature = "duration", since = "1.3.0")] #[inline] pub fn from_secs(secs: u64) -> Duration { @@ -73,6 +92,14 @@ impl Duration { } /// Creates a new `Duration` from the specified number of milliseconds. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let five_seconds = Duration::from_millis(5000); + /// ``` #[stable(feature = "duration", since = "1.3.0")] #[inline] pub fn from_millis(millis: u64) -> Duration { @@ -81,26 +108,46 @@ impl Duration { Duration { secs: secs, nanos: nanos } } - /// Returns the number of whole seconds represented by this duration. + /// Returns the number of whole seconds represented by this `Duration`. /// /// The extra precision represented by this duration is ignored (i.e. extra /// nanoseconds are not represented in the returned value). + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let five_seconds = Duration::new(5, 0); + /// assert_eq!(five_seconds.as_secs(), 5); + /// ``` #[stable(feature = "duration", since = "1.3.0")] #[inline] pub fn as_secs(&self) -> u64 { self.secs } - /// Returns the nanosecond precision represented by this duration. + /// Returns the nanosecond precision represented by this `Duration`. /// /// This method does **not** return the length of the duration when /// represented by nanoseconds. The returned number always represents a /// fractional portion of a second (i.e. it is less than one billion). + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::from_millis(5010); + /// assert_eq!(duration.subsec_nanos(), 10000000); + /// ``` #[stable(feature = "duration", since = "1.3.0")] #[inline] pub fn subsec_nanos(&self) -> u32 { self.nanos } - /// Checked duration addition. Computes `self + other`, returning `None` + /// Checked `Duration` addition. Computes `self + other`, returning [`None`] /// if overflow occurred. /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// /// # Examples /// /// Basic usage: @@ -136,9 +183,11 @@ impl Duration { } } - /// Checked duration subtraction. Computes `self + other`, returning `None` + /// Checked `Duration` subtraction. Computes `self - other`, returning [`None`] /// if the result would be negative or if underflow occurred. /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// /// # Examples /// /// Basic usage: @@ -172,8 +221,10 @@ impl Duration { } } - /// Checked duration multiplication. Computes `self * other`, returning - /// `None` if underflow or overflow occurred. + /// Checked `Duration` multiplication. Computes `self * other`, returning + /// [`None`] if overflow occurred. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None /// /// # Examples /// @@ -207,8 +258,10 @@ impl Duration { } } - /// Checked duration division. Computes `self / other`, returning `None` - /// if `other == 0` or the operation results in underflow or overflow. + /// Checked `Duration` division. Computes `self / other`, returning [`None`] + /// if `other == 0`. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None /// /// # Examples /// diff --git a/src/libstd_unicode/char.rs b/src/libstd_unicode/char.rs index 94599216db6..53dafadb5d5 100644 --- a/src/libstd_unicode/char.rs +++ b/src/libstd_unicode/char.rs @@ -448,8 +448,6 @@ impl char { /// In both of these examples, 'ß' takes two bytes to encode. /// /// ``` - /// #![feature(unicode)] - /// /// let mut b = [0; 2]; /// /// let result = 'ß'.encode_utf8(&mut b); @@ -462,7 +460,6 @@ impl char { /// A buffer that's too small: /// /// ``` - /// #![feature(unicode)] /// use std::thread; /// /// let result = thread::spawn(|| { @@ -474,9 +471,7 @@ impl char { /// /// assert!(result.is_err()); /// ``` - #[unstable(feature = "unicode", - reason = "pending decision about Iterator/Writer/Reader", - issue = "27784")] + #[stable(feature = "unicode_encode_char", since = "1.15.0")] #[inline] pub fn encode_utf8(self, dst: &mut [u8]) -> &mut str { C::encode_utf8(self, dst) @@ -495,8 +490,6 @@ impl char { /// In both of these examples, '𝕊' takes two `u16`s to encode. /// /// ``` - /// #![feature(unicode)] - /// /// let mut b = [0; 2]; /// /// let result = '𝕊'.encode_utf16(&mut b); @@ -507,7 +500,6 @@ impl char { /// A buffer that's too small: /// /// ``` - /// #![feature(unicode)] /// use std::thread; /// /// let result = thread::spawn(|| { @@ -519,9 +511,7 @@ impl char { /// /// assert!(result.is_err()); /// ``` - #[unstable(feature = "unicode", - reason = "pending decision about Iterator/Writer/Reader", - issue = "27784")] + #[stable(feature = "unicode_encode_char", since = "1.15.0")] #[inline] pub fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] { C::encode_utf16(self, dst) diff --git a/src/libstd_unicode/lib.rs b/src/libstd_unicode/lib.rs index b086658ee0d..11724e74cda 100644 --- a/src/libstd_unicode/lib.rs +++ b/src/libstd_unicode/lib.rs @@ -39,7 +39,6 @@ #![feature(lang_items)] #![feature(staged_api)] #![feature(try_from)] -#![feature(unicode)] mod tables; mod u_str; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 2a911aceb9d..a5abdd922d6 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -47,10 +47,14 @@ impl Ident { Ident { name: name, ctxt: SyntaxContext::empty() } } - /// Maps a string to an identifier with an empty syntax context. - pub fn from_str(s: &str) -> Ident { - Ident::with_empty_ctxt(Symbol::intern(s)) - } + /// Maps a string to an identifier with an empty syntax context. + pub fn from_str(s: &str) -> Ident { + Ident::with_empty_ctxt(Symbol::intern(s)) + } + + pub fn unhygienize(&self) -> Ident { + Ident { name: self.name, ctxt: SyntaxContext::empty() } + } } impl fmt::Debug for Ident { @@ -107,10 +111,8 @@ pub struct LifetimeDef { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)] pub struct Path { pub span: Span, - /// A `::foo` path, is relative to the crate root rather than current - /// module (like paths in an import). - pub global: bool, /// The segments in the path: the things separated by `::`. + /// Global paths begin with `keywords::CrateRoot`. pub segments: Vec<PathSegment>, } @@ -132,14 +134,21 @@ impl Path { pub fn from_ident(s: Span, identifier: Ident) -> Path { Path { span: s, - global: false, - segments: vec![ - PathSegment { - identifier: identifier, - parameters: PathParameters::none() - } - ], + segments: vec![identifier.into()], + } + } + + pub fn default_to_global(mut self) -> Path { + let name = self.segments[0].identifier.name; + if !self.is_global() && name != "$crate" && + name != keywords::SelfValue.name() && name != keywords::Super.name() { + self.segments.insert(0, PathSegment::crate_root()); } + self + } + + pub fn is_global(&self) -> bool { + !self.segments.is_empty() && self.segments[0].identifier.name == keywords::CrateRoot.name() } } @@ -156,7 +165,24 @@ pub struct PathSegment { /// this is more than just simple syntactic sugar; the use of /// parens affects the region binding rules, so we preserve the /// distinction. - pub parameters: PathParameters, + /// The `Option<P<..>>` wrapper is purely a size optimization; + /// `None` is used to represent both `Path` and `Path<>`. + pub parameters: Option<P<PathParameters>>, +} + +impl From<Ident> for PathSegment { + fn from(id: Ident) -> Self { + PathSegment { identifier: id, parameters: None } + } +} + +impl PathSegment { + pub fn crate_root() -> Self { + PathSegment { + identifier: keywords::CrateRoot.ident(), + parameters: None, + } + } } /// Parameters of a path segment. @@ -170,79 +196,8 @@ pub enum PathParameters { Parenthesized(ParenthesizedParameterData), } -impl PathParameters { - pub fn none() -> PathParameters { - PathParameters::AngleBracketed(AngleBracketedParameterData { - lifetimes: Vec::new(), - types: P::new(), - bindings: P::new(), - }) - } - - pub fn is_empty(&self) -> bool { - match *self { - PathParameters::AngleBracketed(ref data) => data.is_empty(), - - // Even if the user supplied no types, something like - // `X()` is equivalent to `X<(),()>`. - PathParameters::Parenthesized(..) => false, - } - } - - pub fn has_lifetimes(&self) -> bool { - match *self { - PathParameters::AngleBracketed(ref data) => !data.lifetimes.is_empty(), - PathParameters::Parenthesized(_) => false, - } - } - - pub fn has_types(&self) -> bool { - match *self { - PathParameters::AngleBracketed(ref data) => !data.types.is_empty(), - PathParameters::Parenthesized(..) => true, - } - } - - /// Returns the types that the user wrote. Note that these do not necessarily map to the type - /// parameters in the parenthesized case. - pub fn types(&self) -> Vec<&P<Ty>> { - match *self { - PathParameters::AngleBracketed(ref data) => { - data.types.iter().collect() - } - PathParameters::Parenthesized(ref data) => { - data.inputs.iter() - .chain(data.output.iter()) - .collect() - } - } - } - - pub fn lifetimes(&self) -> Vec<&Lifetime> { - match *self { - PathParameters::AngleBracketed(ref data) => { - data.lifetimes.iter().collect() - } - PathParameters::Parenthesized(_) => { - Vec::new() - } - } - } - - pub fn bindings(&self) -> Vec<&TypeBinding> { - match *self { - PathParameters::AngleBracketed(ref data) => { - data.bindings.iter().collect() - } - PathParameters::Parenthesized(_) => { - Vec::new() - } - } - } -} - /// A path like `Foo<'a, T>` -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Default)] pub struct AngleBracketedParameterData { /// The lifetime parameters for this path segment. pub lifetimes: Vec<Lifetime>, @@ -254,9 +209,10 @@ pub struct AngleBracketedParameterData { pub bindings: P<[TypeBinding]>, } -impl AngleBracketedParameterData { - fn is_empty(&self) -> bool { - self.lifetimes.is_empty() && self.types.is_empty() && self.bindings.is_empty() +impl Into<Option<P<PathParameters>>> for AngleBracketedParameterData { + fn into(self) -> Option<P<PathParameters>> { + let empty = self.lifetimes.is_empty() && self.types.is_empty() && self.bindings.is_empty(); + if empty { None } else { Some(P(PathParameters::AngleBracketed(self))) } } } @@ -1822,10 +1778,6 @@ impl VariantData { } } -/* - FIXME (#3300): Should allow items to be anonymous. Right now - we just use dummy names for anon items. - */ /// An item /// /// The name might be a dummy name in case of anonymous items @@ -1968,8 +1920,6 @@ pub struct MacroDef { pub attrs: Vec<Attribute>, pub id: NodeId, pub span: Span, - pub imported_from: Option<Ident>, - pub allow_internal_unstable: bool, pub body: Vec<TokenTree>, } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 45c120e0b95..c31bcfbd869 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -29,7 +29,6 @@ use symbol::Symbol; use util::ThinVec; use std::cell::{RefCell, Cell}; -use std::collections::HashSet; thread_local! { static USED_ATTRS: RefCell<Vec<u64>> = RefCell::new(Vec::new()); @@ -372,16 +371,6 @@ pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute } } -pub fn mk_doc_attr_outer(id: AttrId, item: MetaItem, is_sugared_doc: bool) -> Attribute { - Attribute { - id: id, - style: ast::AttrStyle::Outer, - value: item, - is_sugared_doc: is_sugared_doc, - span: DUMMY_SP, - } -} - pub fn mk_sugared_doc_attr(id: AttrId, text: Symbol, lo: BytePos, hi: BytePos) -> Attribute { let style = doc_comment_style(&text.as_str()); @@ -421,13 +410,6 @@ pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) -> Option<S .and_then(|at| at.value_str()) } -pub fn last_meta_item_value_str_by_name(items: &[MetaItem], name: &str) -> Option<Symbol> { - items.iter() - .rev() - .find(|mi| mi.check_name(name)) - .and_then(|i| i.value_str()) -} - /* Higher-level applications */ pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> { @@ -856,18 +838,6 @@ pub fn find_deprecation(diagnostic: &Handler, attrs: &[Attribute], find_deprecation_generic(diagnostic, attrs.iter(), item_sp) } -pub fn require_unique_names(diagnostic: &Handler, metas: &[MetaItem]) { - let mut set = HashSet::new(); - for meta in metas { - let name = meta.name(); - - if !set.insert(name.clone()) { - panic!(diagnostic.span_fatal(meta.span, - &format!("duplicate meta item `{}`", name))); - } - } -} - /// Parse #[repr(...)] forms. /// diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index ddbca47429d..68d261c64f8 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -217,8 +217,7 @@ pub trait IdentMacroExpander { cx: &'cx mut ExtCtxt, sp: Span, ident: ast::Ident, - token_tree: Vec<tokenstream::TokenTree>, - attrs: Vec<ast::Attribute>) + token_tree: Vec<tokenstream::TokenTree>) -> Box<MacResult+'cx>; } @@ -234,8 +233,7 @@ impl<F> IdentMacroExpander for F cx: &'cx mut ExtCtxt, sp: Span, ident: ast::Ident, - token_tree: Vec<tokenstream::TokenTree>, - _attrs: Vec<ast::Attribute>) + token_tree: Vec<tokenstream::TokenTree>) -> Box<MacResult+'cx> { (*self)(cx, sp, ident, token_tree) @@ -518,9 +516,9 @@ pub trait Resolver { fn next_node_id(&mut self) -> ast::NodeId; fn get_module_scope(&mut self, id: ast::NodeId) -> Mark; fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item>; + fn is_whitelisted_legacy_custom_derive(&self, name: Name) -> bool; fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion); - fn add_macro(&mut self, scope: Mark, def: ast::MacroDef, export: bool); fn add_ext(&mut self, ident: ast::Ident, ext: Rc<SyntaxExtension>); fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>); @@ -542,9 +540,9 @@ impl Resolver for DummyResolver { fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID } fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() } fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> { item } + fn is_whitelisted_legacy_custom_derive(&self, _name: Name) -> bool { false } fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {} - fn add_macro(&mut self, _scope: Mark, _def: ast::MacroDef, _export: bool) {} fn add_ext(&mut self, _ident: ast::Ident, _ext: Rc<SyntaxExtension>) {} fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec<Mark>) {} diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index a208b934d79..7584fa3916d 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -11,7 +11,7 @@ use abi::Abi; use ast::{self, Ident, Generics, Expr, BlockCheckMode, UnOp, PatKind}; use attr; -use syntax_pos::{Span, DUMMY_SP, Pos}; +use syntax_pos::{Span, DUMMY_SP}; use codemap::{dummy_spanned, respan, Spanned}; use ext::base::ExtCtxt; use ptr::P; @@ -322,24 +322,24 @@ impl<'a> AstBuilder for ExtCtxt<'a> { bindings: Vec<ast::TypeBinding> ) -> ast::Path { let last_identifier = idents.pop().unwrap(); - let mut segments: Vec<ast::PathSegment> = idents.into_iter() - .map(|ident| { - ast::PathSegment { - identifier: ident, - parameters: ast::PathParameters::none(), - } - }).collect(); - segments.push(ast::PathSegment { - identifier: last_identifier, - parameters: ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData { + let mut segments: Vec<ast::PathSegment> = Vec::new(); + if global { + segments.push(ast::PathSegment::crate_root()); + } + + segments.extend(idents.into_iter().map(Into::into)); + let parameters = if lifetimes.is_empty() && types.is_empty() && bindings.is_empty() { + None + } else { + Some(P(ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData { lifetimes: lifetimes, types: P::from_vec(types), bindings: P::from_vec(bindings), - }) - }); + }))) + }; + segments.push(ast::PathSegment { identifier: last_identifier, parameters: parameters }); ast::Path { span: sp, - global: global, segments: segments, } } @@ -367,13 +367,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> { bindings: Vec<ast::TypeBinding>) -> (ast::QSelf, ast::Path) { let mut path = trait_path; + let parameters = ast::AngleBracketedParameterData { + lifetimes: lifetimes, + types: P::from_vec(types), + bindings: P::from_vec(bindings), + }; path.segments.push(ast::PathSegment { identifier: ident, - parameters: ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData { - lifetimes: lifetimes, - types: P::from_vec(types), - bindings: P::from_vec(bindings), - }) + parameters: Some(P(ast::PathParameters::AngleBracketed(parameters))), }); (ast::QSelf { @@ -659,23 +660,11 @@ impl<'a> AstBuilder for ExtCtxt<'a> { } fn expr_field_access(&self, sp: Span, expr: P<ast::Expr>, ident: ast::Ident) -> P<ast::Expr> { - let field_span = Span { - lo: sp.lo - Pos::from_usize(ident.name.as_str().len()), - hi: sp.hi, - expn_id: sp.expn_id, - }; - - let id = Spanned { node: ident, span: field_span }; + let id = Spanned { node: ident, span: sp }; self.expr(sp, ast::ExprKind::Field(expr, id)) } fn expr_tup_field_access(&self, sp: Span, expr: P<ast::Expr>, idx: usize) -> P<ast::Expr> { - let field_span = Span { - lo: sp.lo - Pos::from_usize(idx.to_string().len()), - hi: sp.hi, - expn_id: sp.expn_id, - }; - - let id = Spanned { node: idx, span: field_span }; + let id = Spanned { node: idx, span: sp }; self.expr(sp, ast::ExprKind::TupField(expr, id)) } fn expr_addr_of(&self, sp: Span, e: P<ast::Expr>) -> P<ast::Expr> { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index e3979926680..5d62175fbf2 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -158,7 +158,6 @@ pub struct Invocation { pub enum InvocationKind { Bang { - attrs: Vec<ast::Attribute>, mac: ast::Mac, ident: Option<Ident>, span: Span, @@ -276,7 +275,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if expansions.len() < depth { expansions.push(Vec::new()); } - expansions[depth - 1].push((mark.as_u32(), expansion)); + expansions[depth - 1].push((mark, expansion)); if !self.cx.ecfg.single_step { invocations.extend(new_invocations.into_iter().rev()); } @@ -287,7 +286,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic); while let Some(expansions) = expansions.pop() { for (mark, expansion) in expansions.into_iter().rev() { - placeholder_expander.add(ast::NodeId::from_u32(mark), expansion); + placeholder_expander.add(mark.as_placeholder_id(), expansion); } } @@ -386,20 +385,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { /// Expand a macro invocation. Returns the result of expansion. fn expand_bang_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion { let (mark, kind) = (invoc.expansion_data.mark, invoc.expansion_kind); - let (attrs, mac, ident, span) = match invoc.kind { - InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span), + let (mac, ident, span) = match invoc.kind { + InvocationKind::Bang { mac, ident, span } => (mac, ident, span), _ => unreachable!(), }; let Mac_ { path, tts, .. } = mac.node; - // Detect use of feature-gated or invalid attributes on macro invoations - // since they will not be detected after macro expansion. - for attr in attrs.iter() { - feature_gate::check_attribute(&attr, &self.cx.parse_sess, - &self.cx.parse_sess.codemap(), - &self.cx.ecfg.features.unwrap()); - } - let extname = path.segments.last().unwrap().identifier.name; let ident = ident.unwrap_or(keywords::Invalid.ident()); let marked_tts = mark_tts(&tts, mark); @@ -440,7 +431,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } }); - kind.make_from(expander.expand(self.cx, span, ident, marked_tts, attrs)) + kind.make_from(expander.expand(self.cx, span, ident, marked_tts)) } MultiDecorator(..) | MultiModifier(..) | SyntaxExtension::AttrProcMacro(..) => { @@ -595,13 +586,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ..self.cx.current_expansion.clone() }, }); - placeholder(expansion_kind, ast::NodeId::from_u32(mark.as_u32())) + placeholder(expansion_kind, mark.as_placeholder_id()) } - fn collect_bang( - &mut self, mac: ast::Mac, attrs: Vec<ast::Attribute>, span: Span, kind: ExpansionKind, - ) -> Expansion { - self.collect(kind, InvocationKind::Bang { attrs: attrs, mac: mac, ident: None, span: span }) + fn collect_bang(&mut self, mac: ast::Mac, span: Span, kind: ExpansionKind) -> Expansion { + self.collect(kind, InvocationKind::Bang { mac: mac, ident: None, span: span }) } fn collect_attr(&mut self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind) @@ -622,6 +611,16 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> { self.cfg.configure(node) } + + // Detect use of feature-gated or invalid attributes on macro invocations + // since they will not be detected after macro expansion. + fn check_attributes(&mut self, attrs: &[ast::Attribute]) { + let codemap = &self.cx.parse_sess.codemap(); + let features = self.cx.ecfg.features.unwrap(); + for attr in attrs.iter() { + feature_gate::check_attribute(&attr, &self.cx.parse_sess, codemap, features); + } + } } // These are pretty nasty. Ideally, we would keep the tokens around, linked from @@ -650,7 +649,7 @@ fn string_to_tts(text: String, parse_sess: &ParseSess) -> Vec<TokenTree> { .new_filemap(String::from("<macro expansion>"), None, text); let lexer = lexer::StringReader::new(&parse_sess.span_diagnostic, filemap); - let mut parser = Parser::new(parse_sess, Box::new(lexer)); + let mut parser = Parser::new(parse_sess, Box::new(lexer), None, false); panictry!(parser.parse_all_token_trees()) } @@ -660,7 +659,8 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { expr.node = self.cfg.configure_expr_kind(expr.node); if let ast::ExprKind::Mac(mac) = expr.node { - self.collect_bang(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr).make_expr() + self.check_attributes(&expr.attrs); + self.collect_bang(mac, expr.span, ExpansionKind::Expr).make_expr() } else { P(noop_fold_expr(expr, self)) } @@ -671,8 +671,8 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { expr.node = self.cfg.configure_expr_kind(expr.node); if let ast::ExprKind::Mac(mac) = expr.node { - self.collect_bang(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr) - .make_opt_expr() + self.check_attributes(&expr.attrs); + self.collect_bang(mac, expr.span, ExpansionKind::OptExpr).make_opt_expr() } else { Some(P(noop_fold_expr(expr, self))) } @@ -685,8 +685,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { } pat.and_then(|pat| match pat.node { - PatKind::Mac(mac) => - self.collect_bang(mac, Vec::new(), pat.span, ExpansionKind::Pat).make_pat(), + PatKind::Mac(mac) => self.collect_bang(mac, pat.span, ExpansionKind::Pat).make_pat(), _ => unreachable!(), }) } @@ -707,8 +706,8 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { }).collect() }; - let mut placeholder = - self.collect_bang(mac, attrs.into(), stmt.span, ExpansionKind::Stmts).make_stmts(); + self.check_attributes(&attrs); + let mut placeholder = self.collect_bang(mac, stmt.span, ExpansionKind::Stmts).make_stmts(); // If this is a macro invocation with a semicolon, then apply that // semicolon to the final statement produced by expansion. @@ -740,18 +739,21 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { match item.node { ast::ItemKind::Mac(..) => { - if match item.node { - ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(), - _ => unreachable!(), - } { - return SmallVector::one(item); - } + self.check_attributes(&item.attrs); + let is_macro_def = if let ItemKind::Mac(ref mac) = item.node { + mac.node.path.segments[0].identifier.name == "macro_rules" + } else { + unreachable!() + }; - item.and_then(|item| match item.node { + item.and_then(|mut item| match item.node { + ItemKind::Mac(_) if is_macro_def => { + item.id = Mark::fresh().as_placeholder_id(); + SmallVector::one(P(item)) + } ItemKind::Mac(mac) => { self.collect(ExpansionKind::Items, InvocationKind::Bang { mac: mac, - attrs: item.attrs, ident: Some(item.ident), span: item.span, }).make_items() @@ -823,7 +825,8 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { match item.node { ast::TraitItemKind::Macro(mac) => { let ast::TraitItem { attrs, span, .. } = item; - self.collect_bang(mac, attrs, span, ExpansionKind::TraitItems).make_trait_items() + self.check_attributes(&attrs); + self.collect_bang(mac, span, ExpansionKind::TraitItems).make_trait_items() } _ => fold::noop_fold_trait_item(item, self), } @@ -841,7 +844,8 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { match item.node { ast::ImplItemKind::Macro(mac) => { let ast::ImplItem { attrs, span, .. } = item; - self.collect_bang(mac, attrs, span, ExpansionKind::ImplItems).make_impl_items() + self.check_attributes(&attrs); + self.collect_bang(mac, span, ExpansionKind::ImplItems).make_impl_items() } _ => fold::noop_fold_impl_item(item, self), } @@ -854,8 +858,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { }; match ty.node { - ast::TyKind::Mac(mac) => - self.collect_bang(mac, Vec::new(), ty.span, ExpansionKind::Ty).make_ty(), + ast::TyKind::Mac(mac) => self.collect_bang(mac, ty.span, ExpansionKind::Ty).make_ty(), _ => unreachable!(), } } diff --git a/src/libsyntax/ext/hygiene.rs b/src/libsyntax/ext/hygiene.rs index 0fd72277cca..2af5c2ea999 100644 --- a/src/libsyntax/ext/hygiene.rs +++ b/src/libsyntax/ext/hygiene.rs @@ -51,7 +51,11 @@ impl Mark { Mark(id.as_u32()) } - pub fn as_u32(&self) -> u32 { + pub fn as_placeholder_id(self) -> NodeId { + NodeId::from_u32(self.0) + } + + pub fn as_u32(self) -> u32 { self.0 } } @@ -115,12 +119,12 @@ impl SyntaxContext { }) } - /// If `ident` is macro expanded, return the source ident from the macro definition - /// and the mark of the expansion that created the macro definition. - pub fn source(self) -> (Self /* source context */, Mark /* source macro */) { - let macro_def_ctxt = self.data().prev_ctxt.data(); - (macro_def_ctxt.prev_ctxt, macro_def_ctxt.outer_mark) - } + /// If `ident` is macro expanded, return the source ident from the macro definition + /// and the mark of the expansion that created the macro definition. + pub fn source(self) -> (Self /* source context */, Mark /* source macro */) { + let macro_def_ctxt = self.data().prev_ctxt.data(); + (macro_def_ctxt.prev_ctxt, macro_def_ctxt.outer_mark) + } } impl fmt::Debug for SyntaxContext { diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index 4fe57a8345e..66555d7d95d 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -12,9 +12,10 @@ use ast; use codemap::{DUMMY_SP, dummy_spanned}; use ext::base::ExtCtxt; use ext::expand::{Expansion, ExpansionKind}; +use ext::hygiene::Mark; use fold::*; use ptr::P; -use symbol::{Symbol, keywords}; +use symbol::keywords; use util::move_map::MoveMap; use util::small_vector::SmallVector; @@ -24,7 +25,7 @@ use std::mem; pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion { fn mac_placeholder() -> ast::Mac { dummy_spanned(ast::Mac_ { - path: ast::Path { span: DUMMY_SP, global: false, segments: Vec::new() }, + path: ast::Path { span: DUMMY_SP, segments: Vec::new() }, tts: Vec::new(), }) } @@ -68,10 +69,6 @@ pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion { } } -pub fn macro_scope_placeholder() -> Expansion { - placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID) -} - pub struct PlaceholderExpander<'a, 'b: 'a> { expansions: HashMap<ast::NodeId, Expansion>, cx: &'a mut ExtCtxt<'b>, @@ -100,11 +97,12 @@ impl<'a, 'b> PlaceholderExpander<'a, 'b> { impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> { fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> { match item.node { - // Scope placeholder - ast::ItemKind::Mac(_) if item.id == ast::DUMMY_NODE_ID => SmallVector::one(item), - ast::ItemKind::Mac(_) => self.remove(item.id).make_items(), - _ => noop_fold_item(item, self), + ast::ItemKind::Mac(ref mac) if !mac.node.path.segments.is_empty() => {} + ast::ItemKind::Mac(_) => return self.remove(item.id).make_items(), + _ => {} } + + noop_fold_item(item, self) } fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector<ast::TraitItem> { @@ -172,10 +170,10 @@ impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> { block.stmts = block.stmts.move_flat_map(|mut stmt| { remaining_stmts -= 1; - // Scope placeholder + // `macro_rules!` macro definition if let ast::StmtKind::Item(ref item) = stmt.node { - if let ast::ItemKind::Mac(..) = item.node { - macros.push(item.ident.ctxt.data().outer_mark); + if let ast::ItemKind::Mac(_) = item.node { + macros.push(Mark::from_placeholder_id(item.id)); return None; } } @@ -208,33 +206,13 @@ impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> { fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod { let mut module = noop_fold_mod(module, self); module.items = module.items.move_flat_map(|item| match item.node { - ast::ItemKind::Mac(_) => None, // remove scope placeholders from modules + ast::ItemKind::Mac(_) if !self.cx.ecfg.keep_macs => None, // remove macro definitions _ => Some(item), }); module } -} -pub fn reconstructed_macro_rules(def: &ast::MacroDef) -> Expansion { - Expansion::Items(SmallVector::one(P(ast::Item { - ident: def.ident, - attrs: def.attrs.clone(), - id: ast::DUMMY_NODE_ID, - node: ast::ItemKind::Mac(ast::Mac { - span: def.span, - node: ast::Mac_ { - path: ast::Path { - span: DUMMY_SP, - global: false, - segments: vec![ast::PathSegment { - identifier: ast::Ident::with_empty_ctxt(Symbol::intern("macro_rules")), - parameters: ast::PathParameters::none(), - }], - }, - tts: def.body.clone(), - } - }), - vis: ast::Visibility::Inherited, - span: def.span, - }))) + fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { + mac + } } diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 39ffab4dc17..2de31166070 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -83,7 +83,7 @@ use syntax_pos::{self, BytePos, mk_sp, Span}; use codemap::Spanned; use errors::FatalError; use parse::lexer::*; //resolve bug? -use parse::ParseSess; +use parse::{Directory, ParseSess}; use parse::parser::{PathStyle, Parser}; use parse::token::{DocComment, MatchNt, SubstNt}; use parse::token::{Token, Nonterminal}; @@ -407,8 +407,9 @@ fn inner_parse_loop(cur_eis: &mut SmallVector<Box<MatcherPos>>, Success(()) } -pub fn parse(sess: &ParseSess, rdr: TtReader, ms: &[TokenTree]) -> NamedParseResult { - let mut parser = Parser::new_with_doc_flag(sess, Box::new(rdr), true); +pub fn parse(sess: &ParseSess, rdr: TtReader, ms: &[TokenTree], directory: Option<Directory>) + -> NamedParseResult { + let mut parser = Parser::new(sess, Box::new(rdr), directory, true); let mut cur_eis = SmallVector::one(initial_matcher_pos(ms.to_owned(), parser.span.lo)); let mut next_eis = Vec::new(); // or proceed normally diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 4164b4a93ec..3abd24b50ba 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -10,14 +10,13 @@ use {ast, attr}; use syntax_pos::{Span, DUMMY_SP}; -use ext::base::{DummyResult, ExtCtxt, MacEager, MacResult, SyntaxExtension}; -use ext::base::{IdentMacroExpander, NormalTT, TTMacroExpander}; +use ext::base::{DummyResult, ExtCtxt, MacResult, SyntaxExtension}; +use ext::base::{NormalTT, TTMacroExpander}; use ext::expand::{Expansion, ExpansionKind}; -use ext::placeholders; use ext::tt::macro_parser::{Success, Error, Failure}; use ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal}; use ext::tt::macro_parser::{parse, parse_failure_msg}; -use parse::ParseSess; +use parse::{Directory, ParseSess}; use parse::lexer::new_tt_reader; use parse::parser::Parser; use parse::token::{self, NtTT, Token}; @@ -116,12 +115,13 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, // rhs has holes ( `$id` and `$(...)` that need filled) let trncbr = new_tt_reader(&cx.parse_sess.span_diagnostic, Some(named_matches), rhs); - let mut p = Parser::new(cx.parse_sess(), Box::new(trncbr)); - let module = &cx.current_expansion.module; - p.directory.path = module.directory.clone(); - p.directory.ownership = cx.current_expansion.directory_ownership; - p.root_module_name = - module.mod_path.last().map(|id| (*id.name.as_str()).to_owned()); + let directory = Directory { + path: cx.current_expansion.module.directory.clone(), + ownership: cx.current_expansion.directory_ownership, + }; + let mut p = Parser::new(cx.parse_sess(), Box::new(trncbr), Some(directory), false); + p.root_module_name = cx.current_expansion.module.mod_path.last() + .map(|id| (*id.name.as_str()).to_owned()); p.check_unknown_macro_variable(); // Let the context choose how to interpret the result. @@ -150,38 +150,6 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, cx.span_fatal(best_fail_spot.substitute_dummy(sp), &best_fail_msg); } -pub struct MacroRulesExpander; -impl IdentMacroExpander for MacroRulesExpander { - fn expand(&self, - cx: &mut ExtCtxt, - span: Span, - ident: ast::Ident, - tts: Vec<tokenstream::TokenTree>, - attrs: Vec<ast::Attribute>) - -> Box<MacResult> { - let export = attr::contains_name(&attrs, "macro_export"); - let def = ast::MacroDef { - ident: ident, - id: ast::DUMMY_NODE_ID, - span: span, - imported_from: None, - body: tts, - allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"), - attrs: attrs, - }; - - // If keep_macs is true, expands to a MacEager::items instead. - let result = if cx.ecfg.keep_macs { - MacEager::items(placeholders::reconstructed_macro_rules(&def).make_items()) - } else { - MacEager::items(placeholders::macro_scope_placeholder().make_items()) - }; - - cx.resolver.add_macro(cx.current_expansion.mark, def, export); - result - } -} - // Note that macro-by-example's input is also matched against a token tree: // $( $lhs:tt => $rhs:tt );+ // @@ -222,7 +190,7 @@ pub fn compile(sess: &ParseSess, def: &ast::MacroDef) -> SyntaxExtension { // Parse the macro_rules! invocation (`none` is for no interpolations): let arg_reader = new_tt_reader(&sess.span_diagnostic, None, def.body.clone()); - let argument_map = match parse(sess, arg_reader, &argument_gram) { + let argument_map = match parse(sess, arg_reader, &argument_gram, None) { Success(m) => m, Failure(sp, tok) => { let s = parse_failure_msg(tok); @@ -281,7 +249,7 @@ pub fn compile(sess: &ParseSess, def: &ast::MacroDef) -> SyntaxExtension { valid: valid, }); - NormalTT(exp, Some(def.span), def.allow_internal_unstable) + NormalTT(exp, Some(def.span), attr::contains_name(&def.attrs, "allow_internal_unstable")) } fn check_lhs_nt_follows(sess: &ParseSess, lhs: &TokenTree) -> bool { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 77c53542dcb..e04cc11f15e 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -304,6 +304,7 @@ declare_features! ( // Allows using `Self` and associated types in struct expressions and patterns. (active, more_struct_aliases, "1.14.0", Some(37544)), + // Allows #[link(..., cfg(..))] (active, link_cfg, "1.14.0", Some(37406)), @@ -314,6 +315,9 @@ declare_features! ( // Allows #[target_feature(...)] (active, target_feature, "1.15.0", None), + + // Allow safe suggestions for potential type conversions. + (active, safe_suggestion, "1.0.0", Some(37384)), ); declare_features! ( diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 6af8efb2a19..9797e0003fc 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -302,23 +302,22 @@ pub fn noop_fold_view_path<T: Folder>(view_path: P<ViewPath>, fld: &mut T) -> P< view_path.map(|Spanned {node, span}| Spanned { node: match node { ViewPathSimple(ident, path) => { - ViewPathSimple(ident, fld.fold_path(path)) + ViewPathSimple(fld.fold_ident(ident), fld.fold_path(path)) } ViewPathGlob(path) => { ViewPathGlob(fld.fold_path(path)) } ViewPathList(path, path_list_idents) => { - ViewPathList(fld.fold_path(path), - path_list_idents.move_map(|path_list_ident| { - Spanned { - node: PathListItem_ { - id: fld.new_id(path_list_ident.node.id), - rename: path_list_ident.node.rename, - name: path_list_ident.node.name, - }, - span: fld.new_span(path_list_ident.span) - } - })) + let path = fld.fold_path(path); + let path_list_idents = path_list_idents.move_map(|path_list_ident| Spanned { + node: PathListItem_ { + id: fld.new_id(path_list_ident.node.id), + rename: path_list_ident.node.rename.map(|ident| fld.fold_ident(ident)), + name: fld.fold_ident(path_list_ident.node.name), + }, + span: fld.new_span(path_list_ident.span) + }); + ViewPathList(path, path_list_idents) } }, span: fld.new_span(span) @@ -345,7 +344,7 @@ pub fn noop_fold_arm<T: Folder>(Arm {attrs, pats, guard, body}: Arm, fld: &mut T pub fn noop_fold_ty_binding<T: Folder>(b: TypeBinding, fld: &mut T) -> TypeBinding { TypeBinding { id: fld.new_id(b.id), - ident: b.ident, + ident: fld.fold_ident(b.ident), ty: fld.fold_ty(b.ty), span: fld.new_span(b.span), } @@ -433,12 +432,11 @@ pub fn noop_fold_usize<T: Folder>(i: usize, _: &mut T) -> usize { i } -pub fn noop_fold_path<T: Folder>(Path {global, segments, span}: Path, fld: &mut T) -> Path { +pub fn noop_fold_path<T: Folder>(Path { segments, span }: Path, fld: &mut T) -> Path { Path { - global: global, segments: segments.move_map(|PathSegment {identifier, parameters}| PathSegment { identifier: fld.fold_ident(identifier), - parameters: fld.fold_path_parameters(parameters), + parameters: parameters.map(|ps| ps.map(|ps| fld.fold_path_parameters(ps))), }), span: fld.new_span(span) } @@ -673,7 +671,7 @@ pub fn noop_fold_ty_param<T: Folder>(tp: TyParam, fld: &mut T) -> TyParam { .collect::<Vec<_>>() .into(), id: fld.new_id(id), - ident: ident, + ident: fld.fold_ident(ident), bounds: fld.fold_bounds(bounds), default: default.map(|x| fld.fold_ty(x)), span: span @@ -1088,7 +1086,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> { let fs = fields.move_map(|f| { Spanned { span: folder.new_span(f.span), node: ast::FieldPat { - ident: f.node.ident, + ident: folder.fold_ident(f.node.ident), pat: folder.fold_pat(f.node.pat), is_shorthand: f.node.is_shorthand, }} diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index e5b66f88958..24178e1f675 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -222,14 +222,14 @@ pub fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>) // it appears to me that the cfg doesn't matter here... indeed, // parsing tt's probably shouldn't require a parser at all. let srdr = lexer::StringReader::new(&sess.span_diagnostic, filemap); - let mut p1 = Parser::new(sess, Box::new(srdr)); + let mut p1 = Parser::new(sess, Box::new(srdr), None, false); panictry!(p1.parse_all_token_trees()) } /// Given tts and the ParseSess, produce a parser pub fn tts_to_parser<'a>(sess: &'a ParseSess, tts: Vec<tokenstream::TokenTree>) -> Parser<'a> { let trdr = lexer::new_tt_reader(&sess.span_diagnostic, None, tts); - let mut p = Parser::new(sess, Box::new(trdr)); + let mut p = Parser::new(sess, Box::new(trdr), None, false); p.check_unknown_macro_variable(); p } @@ -633,13 +633,7 @@ mod tests { id: ast::DUMMY_NODE_ID, node: ast::ExprKind::Path(None, ast::Path { span: sp(0, 1), - global: false, - segments: vec![ - ast::PathSegment { - identifier: Ident::from_str("a"), - parameters: ast::PathParameters::none(), - } - ], + segments: vec![Ident::from_str("a").into()], }), span: sp(0, 1), attrs: ThinVec::new(), @@ -651,19 +645,11 @@ mod tests { P(ast::Expr { id: ast::DUMMY_NODE_ID, node: ast::ExprKind::Path(None, ast::Path { - span: sp(0, 6), - global: true, - segments: vec![ - ast::PathSegment { - identifier: Ident::from_str("a"), - parameters: ast::PathParameters::none(), - }, - ast::PathSegment { - identifier: Ident::from_str("b"), - parameters: ast::PathParameters::none(), - } - ] - }), + span: sp(0, 6), + segments: vec![ast::PathSegment::crate_root(), + Ident::from_str("a").into(), + Ident::from_str("b").into()] + }), span: sp(0, 6), attrs: ThinVec::new(), })) @@ -771,13 +757,7 @@ mod tests { id: ast::DUMMY_NODE_ID, node:ast::ExprKind::Path(None, ast::Path{ span: sp(7, 8), - global: false, - segments: vec![ - ast::PathSegment { - identifier: Ident::from_str("d"), - parameters: ast::PathParameters::none(), - } - ], + segments: vec![Ident::from_str("d").into()], }), span:sp(7,8), attrs: ThinVec::new(), @@ -794,13 +774,7 @@ mod tests { id: ast::DUMMY_NODE_ID, node: ast::ExprKind::Path(None, ast::Path { span:sp(0,1), - global:false, - segments: vec![ - ast::PathSegment { - identifier: Ident::from_str("b"), - parameters: ast::PathParameters::none(), - } - ], + segments: vec![Ident::from_str("b").into()], }), span: sp(0,1), attrs: ThinVec::new()})), @@ -841,13 +815,7 @@ mod tests { ty: P(ast::Ty{id: ast::DUMMY_NODE_ID, node: ast::TyKind::Path(None, ast::Path{ span:sp(10,13), - global:false, - segments: vec![ - ast::PathSegment { - identifier: Ident::from_str("i32"), - parameters: ast::PathParameters::none(), - } - ], + segments: vec![Ident::from_str("i32").into()], }), span:sp(10,13) }), @@ -889,14 +857,7 @@ mod tests { node: ast::ExprKind::Path(None, ast::Path{ span:sp(17,18), - global:false, - segments: vec![ - ast::PathSegment { - identifier: Ident::from_str("b"), - parameters: - ast::PathParameters::none(), - } - ], + segments: vec![Ident::from_str("b").into()], }), span: sp(17,18), attrs: ThinVec::new()})), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index bdd1606805f..a0ed50b33a4 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -267,12 +267,11 @@ impl From<P<Expr>> for LhsExpr { } impl<'a> Parser<'a> { - pub fn new(sess: &'a ParseSess, rdr: Box<Reader+'a>) -> Self { - Parser::new_with_doc_flag(sess, rdr, false) - } - - pub fn new_with_doc_flag(sess: &'a ParseSess, rdr: Box<Reader+'a>, desugar_doc_comments: bool) - -> Self { + pub fn new(sess: &'a ParseSess, + rdr: Box<Reader+'a>, + directory: Option<Directory>, + desugar_doc_comments: bool) + -> Self { let mut parser = Parser { reader: rdr, sess: sess, @@ -298,7 +297,9 @@ impl<'a> Parser<'a> { let tok = parser.next_tok(); parser.token = tok.tok; parser.span = tok.sp; - if parser.span != syntax_pos::DUMMY_SP { + if let Some(directory) = directory { + parser.directory = directory; + } else if parser.span != syntax_pos::DUMMY_SP { parser.directory.path = PathBuf::from(sess.codemap().span_to_filename(parser.span)); parser.directory.path.pop(); } @@ -1613,7 +1614,6 @@ impl<'a> Parser<'a> { } else { ast::Path { span: span, - global: false, segments: vec![] } }; @@ -1657,7 +1657,7 @@ impl<'a> Parser<'a> { // Parse any number of segments and bound sets. A segment is an // identifier followed by an optional lifetime and a set of types. // A bound set is a set of type parameter bounds. - let segments = match mode { + let mut segments = match mode { PathStyle::Type => { self.parse_path_segments_without_colons()? } @@ -1669,13 +1669,16 @@ impl<'a> Parser<'a> { } }; + if is_global { + segments.insert(0, ast::PathSegment::crate_root()); + } + // Assemble the span. let span = mk_sp(lo, self.prev_span.hi); // Assemble the result. Ok(ast::Path { span: span, - global: is_global, segments: segments, }) } @@ -1704,12 +1707,11 @@ impl<'a> Parser<'a> { // Parse types, optionally. let parameters = if self.eat_lt() { let (lifetimes, types, bindings) = self.parse_generic_values_after_lt()?; - - ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData { + ast::AngleBracketedParameterData { lifetimes: lifetimes, types: P::from_vec(types), bindings: P::from_vec(bindings), - }) + }.into() } else if self.eat(&token::OpenDelim(token::Paren)) { let lo = self.prev_span.lo; @@ -1726,18 +1728,17 @@ impl<'a> Parser<'a> { let hi = self.prev_span.hi; - ast::PathParameters::Parenthesized(ast::ParenthesizedParameterData { + Some(P(ast::PathParameters::Parenthesized(ast::ParenthesizedParameterData { span: mk_sp(lo, hi), inputs: inputs, output: output_ty, - }) + }))) } else { - ast::PathParameters::none() + None }; // Assemble and push the result. - segments.push(ast::PathSegment { identifier: identifier, - parameters: parameters }); + segments.push(ast::PathSegment { identifier: identifier, parameters: parameters }); // Continue only if we see a `::` if !self.eat(&token::ModSep) { @@ -1756,10 +1757,7 @@ impl<'a> Parser<'a> { // If we do not see a `::`, stop. if !self.eat(&token::ModSep) { - segments.push(ast::PathSegment { - identifier: identifier, - parameters: ast::PathParameters::none() - }); + segments.push(identifier.into()); return Ok(segments); } @@ -1767,14 +1765,13 @@ impl<'a> Parser<'a> { if self.eat_lt() { // Consumed `a::b::<`, go look for types let (lifetimes, types, bindings) = self.parse_generic_values_after_lt()?; - let parameters = ast::AngleBracketedParameterData { - lifetimes: lifetimes, - types: P::from_vec(types), - bindings: P::from_vec(bindings), - }; segments.push(ast::PathSegment { identifier: identifier, - parameters: ast::PathParameters::AngleBracketed(parameters), + parameters: ast::AngleBracketedParameterData { + lifetimes: lifetimes, + types: P::from_vec(types), + bindings: P::from_vec(bindings), + }.into(), }); // Consumed `a::b::<T,U>`, check for `::` before proceeding @@ -1783,10 +1780,7 @@ impl<'a> Parser<'a> { } } else { // Consumed `a::`, go look for `b` - segments.push(ast::PathSegment { - identifier: identifier, - parameters: ast::PathParameters::none(), - }); + segments.push(identifier.into()); } } } @@ -1801,10 +1795,7 @@ impl<'a> Parser<'a> { let identifier = self.parse_path_segment_ident()?; // Assemble and push the result. - segments.push(ast::PathSegment { - identifier: identifier, - parameters: ast::PathParameters::none() - }); + segments.push(identifier.into()); // If we do not see a `::` or see `::{`/`::*`, stop. if !self.check(&token::ModSep) || self.is_import_coupler() { @@ -4173,7 +4164,7 @@ impl<'a> Parser<'a> { })); self.bump(); } - token::ModSep | token::Ident(..) => { + _ if self.token.is_path_start() || self.token.is_keyword(keywords::For) => { let poly_trait_ref = self.parse_poly_trait_ref()?; let modifier = if ate_question { TraitBoundModifier::Maybe @@ -4377,6 +4368,23 @@ impl<'a> Parser<'a> { return Ok(where_clause); } + // This is a temporary hack. + // + // We are considering adding generics to the `where` keyword as an alternative higher-rank + // parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking + // change, for now we refuse to parse `where < (ident | lifetime) (> | , | :)`. + if token::Lt == self.token { + let ident_or_lifetime = self.look_ahead(1, |t| t.is_ident() || t.is_lifetime()); + if ident_or_lifetime { + let gt_comma_or_colon = self.look_ahead(2, |t| { + *t == token::Gt || *t == token::Comma || *t == token::Colon + }); + if gt_comma_or_colon { + self.span_err(self.span, "syntax `where<T>` is reserved for future use"); + } + } + } + let mut parsed_something = false; loop { let lo = self.span.lo; @@ -5191,7 +5199,7 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(keywords::Crate) { pub_crate(self) } else { - let path = self.parse_path(PathStyle::Mod)?; + let path = self.parse_path(PathStyle::Mod)?.default_to_global(); self.expect(&token::CloseDelim(token::Paren))?; Ok(Visibility::Restricted { path: P(path), id: ast::DUMMY_NODE_ID }) } @@ -6079,9 +6087,9 @@ impl<'a> Parser<'a> { if self.check(&token::OpenDelim(token::Brace)) || self.check(&token::BinOp(token::Star)) || self.is_import_coupler() { // `{foo, bar}`, `::{foo, bar}`, `*`, or `::*`. + self.eat(&token::ModSep); let prefix = ast::Path { - global: self.eat(&token::ModSep), - segments: Vec::new(), + segments: vec![ast::PathSegment::crate_root()], span: mk_sp(lo, self.span.hi), }; let view_path_kind = if self.eat(&token::BinOp(token::Star)) { @@ -6091,7 +6099,7 @@ impl<'a> Parser<'a> { }; Ok(P(spanned(lo, self.span.hi, view_path_kind))) } else { - let prefix = self.parse_path(PathStyle::Mod)?; + let prefix = self.parse_path(PathStyle::Mod)?.default_to_global(); if self.is_import_coupler() { // `foo::bar::{a, b}` or `foo::bar::*` self.bump(); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index c28b9d00501..7558f0256da 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -371,7 +371,7 @@ pub fn fn_block_to_string(p: &ast::FnDecl) -> String { } pub fn path_to_string(p: &ast::Path) -> String { - to_string(|s| s.print_path(p, false, 0)) + to_string(|s| s.print_path(p, false, 0, false)) } pub fn ident_to_string(id: ast::Ident) -> String { @@ -435,7 +435,8 @@ pub fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { match *vis { ast::Visibility::Public => format!("pub {}", s), ast::Visibility::Crate(_) => format!("pub(crate) {}", s), - ast::Visibility::Restricted { ref path, .. } => format!("pub({}) {}", path, s), + ast::Visibility::Restricted { ref path, .. } => + format!("pub({}) {}", to_string(|s| s.print_path(path, false, 0, true)), s), ast::Visibility::Inherited => s.to_string() } } @@ -1021,7 +1022,7 @@ impl<'a> State<'a> { &generics)); } ast::TyKind::Path(None, ref path) => { - try!(self.print_path(path, false, 0)); + try!(self.print_path(path, false, 0, false)); } ast::TyKind::Path(Some(ref qself), ref path) => { try!(self.print_qpath(path, qself, false)) @@ -1332,7 +1333,7 @@ impl<'a> State<'a> { } ast::ItemKind::Mac(codemap::Spanned { ref node, .. }) => { try!(self.print_visibility(&item.vis)); - try!(self.print_path(&node.path, false, 0)); + try!(self.print_path(&node.path, false, 0, false)); try!(word(&mut self.s, "! ")); try!(self.print_ident(item.ident)); try!(self.cbox(INDENT_UNIT)); @@ -1347,7 +1348,7 @@ impl<'a> State<'a> { } fn print_trait_ref(&mut self, t: &ast::TraitRef) -> io::Result<()> { - self.print_path(&t.path, false, 0) + self.print_path(&t.path, false, 0, false) } fn print_formal_lifetime_list(&mut self, lifetimes: &[ast::LifetimeDef]) -> io::Result<()> { @@ -1405,8 +1406,10 @@ impl<'a> State<'a> { match *vis { ast::Visibility::Public => self.word_nbsp("pub"), ast::Visibility::Crate(_) => self.word_nbsp("pub(crate)"), - ast::Visibility::Restricted { ref path, .. } => - self.word_nbsp(&format!("pub({})", path)), + ast::Visibility::Restricted { ref path, .. } => { + let path = to_string(|s| s.print_path(path, false, 0, true)); + self.word_nbsp(&format!("pub({})", path)) + } ast::Visibility::Inherited => Ok(()) } } @@ -1571,7 +1574,7 @@ impl<'a> State<'a> { } ast::TraitItemKind::Macro(codemap::Spanned { ref node, .. }) => { // code copied from ItemKind::Mac: - self.print_path(&node.path, false, 0)?; + self.print_path(&node.path, false, 0, false)?; word(&mut self.s, "! ")?; self.cbox(INDENT_UNIT)?; self.popen()?; @@ -1607,7 +1610,7 @@ impl<'a> State<'a> { } ast::ImplItemKind::Macro(codemap::Spanned { ref node, .. }) => { // code copied from ItemKind::Mac: - try!(self.print_path(&node.path, false, 0)); + try!(self.print_path(&node.path, false, 0, false)); try!(word(&mut self.s, "! ")); try!(self.cbox(INDENT_UNIT)); try!(self.popen()); @@ -1793,7 +1796,7 @@ impl<'a> State<'a> { pub fn print_mac(&mut self, m: &ast::Mac, delim: token::DelimToken) -> io::Result<()> { - try!(self.print_path(&m.node.path, false, 0)); + try!(self.print_path(&m.node.path, false, 0, false)); try!(word(&mut self.s, "!")); match delim { token::Paren => try!(self.popen()), @@ -1885,7 +1888,7 @@ impl<'a> State<'a> { fields: &[ast::Field], wth: &Option<P<ast::Expr>>, attrs: &[Attribute]) -> io::Result<()> { - try!(self.print_path(path, true, 0)); + try!(self.print_path(path, true, 0, false)); try!(word(&mut self.s, "{")); try!(self.print_inner_attributes_inline(attrs)); try!(self.commasep_cmnt( @@ -2186,7 +2189,7 @@ impl<'a> State<'a> { } } ast::ExprKind::Path(None, ref path) => { - try!(self.print_path(path, true, 0)) + try!(self.print_path(path, true, 0, false)) } ast::ExprKind::Path(Some(ref qself), ref path) => { try!(self.print_qpath(path, qself, true)) @@ -2334,22 +2337,27 @@ impl<'a> State<'a> { fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, - depth: usize) + depth: usize, + defaults_to_global: bool) -> io::Result<()> { try!(self.maybe_print_comment(path.span.lo)); - let mut first = !path.global; - for segment in &path.segments[..path.segments.len()-depth] { - if first { - first = false - } else { + let mut segments = path.segments[..path.segments.len()-depth].iter(); + if defaults_to_global && path.is_global() { + segments.next(); + } + for (i, segment) in segments.enumerate() { + if i > 0 { try!(word(&mut self.s, "::")) } - - try!(self.print_ident(segment.identifier)); - - try!(self.print_path_parameters(&segment.parameters, colons_before_params)); + if segment.identifier.name != keywords::CrateRoot.name() && + segment.identifier.name != "$crate" { + try!(self.print_ident(segment.identifier)); + if let Some(ref parameters) = segment.parameters { + try!(self.print_path_parameters(parameters, colons_before_params)); + } + } } Ok(()) @@ -2367,13 +2375,16 @@ impl<'a> State<'a> { try!(space(&mut self.s)); try!(self.word_space("as")); let depth = path.segments.len() - qself.position; - try!(self.print_path(&path, false, depth)); + try!(self.print_path(&path, false, depth, false)); } try!(word(&mut self.s, ">")); try!(word(&mut self.s, "::")); let item_segment = path.segments.last().unwrap(); try!(self.print_ident(item_segment.identifier)); - self.print_path_parameters(&item_segment.parameters, colons_before_params) + match item_segment.parameters { + Some(ref parameters) => self.print_path_parameters(parameters, colons_before_params), + None => Ok(()), + } } fn print_path_parameters(&mut self, @@ -2381,10 +2392,6 @@ impl<'a> State<'a> { colons_before_params: bool) -> io::Result<()> { - if parameters.is_empty() { - return Ok(()); - } - if colons_before_params { try!(word(&mut self.s, "::")) } @@ -2471,7 +2478,7 @@ impl<'a> State<'a> { } } PatKind::TupleStruct(ref path, ref elts, ddpos) => { - try!(self.print_path(path, true, 0)); + try!(self.print_path(path, true, 0, false)); try!(self.popen()); if let Some(ddpos) = ddpos { try!(self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))); @@ -2489,13 +2496,13 @@ impl<'a> State<'a> { try!(self.pclose()); } PatKind::Path(None, ref path) => { - try!(self.print_path(path, true, 0)); + try!(self.print_path(path, true, 0, false)); } PatKind::Path(Some(ref qself), ref path) => { try!(self.print_qpath(path, qself, false)); } PatKind::Struct(ref path, ref fields, etc) => { - try!(self.print_path(path, true, 0)); + try!(self.print_path(path, true, 0, false)); try!(self.nbsp()); try!(self.word_space("{")); try!(self.commasep_cmnt( @@ -2842,7 +2849,7 @@ impl<'a> State<'a> { try!(self.print_lifetime_bounds(lifetime, bounds)); } ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref path, ref ty, ..}) => { - try!(self.print_path(path, false, 0)); + try!(self.print_path(path, false, 0, false)); try!(space(&mut self.s)); try!(self.word_space("=")); try!(self.print_type(&ty)); @@ -2856,7 +2863,7 @@ impl<'a> State<'a> { pub fn print_view_path(&mut self, vp: &ast::ViewPath) -> io::Result<()> { match vp.node { ast::ViewPathSimple(ident, ref path) => { - try!(self.print_path(path, false, 0)); + try!(self.print_path(path, false, 0, true)); if path.segments.last().unwrap().identifier.name != ident.name { @@ -2869,7 +2876,7 @@ impl<'a> State<'a> { } ast::ViewPathGlob(ref path) => { - try!(self.print_path(path, false, 0)); + try!(self.print_path(path, false, 0, true)); word(&mut self.s, "::*") } @@ -2877,7 +2884,7 @@ impl<'a> State<'a> { if path.segments.is_empty() { try!(word(&mut self.s, "{")); } else { - try!(self.print_path(path, false, 0)); + try!(self.print_path(path, false, 0, true)); try!(word(&mut self.s, "::{")); } try!(self.commasep(Inconsistent, &idents[..], |s, w| { diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 6a291ad9c40..68d807b24a7 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -80,10 +80,8 @@ pub fn maybe_inject_crates_ref(sess: &ParseSess, }], vis: ast::Visibility::Inherited, node: ast::ItemKind::Use(P(codemap::dummy_spanned(ast::ViewPathGlob(ast::Path { - global: false, - segments: vec![name, "prelude", "v1"].into_iter().map(|name| ast::PathSegment { - identifier: ast::Ident::from_str(name), - parameters: ast::PathParameters::none(), + segments: ["{{root}}", name, "prelude", "v1"].into_iter().map(|name| { + ast::Ident::from_str(name).into() }).collect(), span: span, })))), diff --git a/src/libsyntax/symbol.rs b/src/libsyntax/symbol.rs index fe9a176179c..c2123ea5a07 100644 --- a/src/libsyntax/symbol.rs +++ b/src/libsyntax/symbol.rs @@ -221,6 +221,9 @@ declare_keywords! { (53, Default, "default") (54, StaticLifetime, "'static") (55, Union, "union") + + // A virtual keyword that resolves to the crate root when used in a lexical scope. + (56, CrateRoot, "{{root}}") } // If an interner exists in TLS, return it. Otherwise, prepare a fresh one. diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index fca89e265e4..b8e0b938814 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -579,11 +579,7 @@ fn nospan<T>(t: T) -> codemap::Spanned<T> { fn path_node(ids: Vec<Ident>) -> ast::Path { ast::Path { span: DUMMY_SP, - global: false, - segments: ids.into_iter().map(|identifier| ast::PathSegment { - identifier: identifier, - parameters: ast::PathParameters::none(), - }).collect() + segments: ids.into_iter().map(Into::into).collect(), } } diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index 0d5dcaf339f..e352e7853c7 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -31,7 +31,7 @@ use ext::base; use ext::tt::macro_parser; use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; use parse::lexer; -use parse; +use parse::{self, Directory}; use parse::token::{self, Token, Lit, Nonterminal}; use print::pprust; use symbol::Symbol; @@ -218,7 +218,11 @@ impl TokenTree { let diag = &cx.parse_sess().span_diagnostic; // `None` is because we're not interpolating let arg_rdr = lexer::new_tt_reader(diag, None, tts.iter().cloned().collect()); - macro_parser::parse(cx.parse_sess(), arg_rdr, mtch) + let directory = Directory { + path: cx.current_expansion.module.directory.clone(), + ownership: cx.current_expansion.directory_ownership, + }; + macro_parser::parse(cx.parse_sess(), arg_rdr, mtch, Some(directory)) } /// Check if this TokenTree is equal to the other, regardless of span information. diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 3e0353d532d..ad29cb50a84 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -178,7 +178,6 @@ pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) { pub fn walk_macro_def<'a, V: Visitor<'a>>(visitor: &mut V, macro_def: &'a MacroDef) { visitor.visit_ident(macro_def.span, macro_def.ident); - walk_opt_ident(visitor, macro_def.span, macro_def.imported_from); walk_list!(visitor, visit_attribute, ¯o_def.attrs); } @@ -384,7 +383,9 @@ pub fn walk_path_segment<'a, V: Visitor<'a>>(visitor: &mut V, path_span: Span, segment: &'a PathSegment) { visitor.visit_ident(path_span, segment.identifier); - visitor.visit_path_parameters(path_span, &segment.parameters); + if let Some(ref parameters) = segment.parameters { + visitor.visit_path_parameters(path_span, parameters); + } } pub fn walk_path_parameters<'a, V>(visitor: &mut V, diff --git a/src/libsyntax_ext/concat_idents.rs b/src/libsyntax_ext/concat_idents.rs index b26e33eb384..1fc1bdff593 100644 --- a/src/libsyntax_ext/concat_idents.rs +++ b/src/libsyntax_ext/concat_idents.rs @@ -59,14 +59,9 @@ pub fn expand_syntax_ext<'cx>(cx: &'cx mut ExtCtxt, impl Result { fn path(&self) -> ast::Path { - let segment = ast::PathSegment { - identifier: self.ident, - parameters: ast::PathParameters::none(), - }; ast::Path { span: self.span, - global: false, - segments: vec![segment], + segments: vec![self.ident.into()], } } } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 51199819dfc..7f187d8d1c1 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -363,15 +363,12 @@ fn find_type_parameters(ty: &ast::Ty, impl<'a, 'b> visit::Visitor<'a> for Visitor<'a, 'b> { fn visit_ty(&mut self, ty: &'a ast::Ty) { - match ty.node { - ast::TyKind::Path(_, ref path) if !path.global => { - if let Some(segment) = path.segments.first() { - if self.ty_param_names.contains(&segment.identifier.name) { - self.types.push(P(ty.clone())); - } + if let ast::TyKind::Path(_, ref path) = ty.node { + if let Some(segment) = path.segments.first() { + if self.ty_param_names.contains(&segment.identifier.name) { + self.types.push(P(ty.clone())); } } - _ => {} } visit::walk_ty(self, ty) diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 535d7de19e3..0511b0d252b 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -175,8 +175,10 @@ pub fn expand_derive(cx: &mut ExtCtxt, feature_gate::GateIssue::Language, feature_gate::EXPLAIN_CUSTOM_DERIVE); } else { - cx.span_warn(titem.span, feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE); let name = Symbol::intern(&format!("derive_{}", tname)); + if !cx.resolver.is_whitelisted_legacy_custom_derive(name) { + cx.span_warn(titem.span, feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE); + } let mitem = cx.meta_word(titem.span, name); new_attributes.push(cx.attribute(mitem.span, mitem)); } diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 66d6c0570ac..e31b29d5cc1 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -50,8 +50,7 @@ pub mod deriving; use std::rc::Rc; use syntax::ast; -use syntax::ext::base::{MacroExpanderFn, NormalTT, IdentTT, MultiModifier, NamedSyntaxExtension}; -use syntax::ext::tt::macro_rules::MacroRulesExpander; +use syntax::ext::base::{MacroExpanderFn, NormalTT, MultiModifier, NamedSyntaxExtension}; use syntax::symbol::Symbol; pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, @@ -61,8 +60,6 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, resolver.add_ext(ast::Ident::with_empty_ctxt(name), Rc::new(ext)); }; - register(Symbol::intern("macro_rules"), IdentTT(Box::new(MacroRulesExpander), None, false)); - macro_rules! register { ($( $name:ident: $f:expr, )*) => { $( register(Symbol::intern(stringify!($name)), diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index 8fbd11a7a6e..5323ace79d8 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -108,6 +108,8 @@ impl<'a> CollectCustomDerives<'a> { impl<'a> Visitor<'a> for CollectCustomDerives<'a> { fn visit_item(&mut self, item: &'a ast::Item) { + let mut attrs = item.attrs.iter().filter(|a| a.check_name("proc_macro_derive")); + // First up, make sure we're checking a bare function. If we're not then // we're just not interested in this item. // @@ -117,10 +119,7 @@ impl<'a> Visitor<'a> for CollectCustomDerives<'a> { ast::ItemKind::Fn(..) => {} _ => { // Check for invalid use of proc_macro_derive - let attr = item.attrs.iter() - .filter(|a| a.check_name("proc_macro_derive")) - .next(); - if let Some(attr) = attr { + if let Some(attr) = attrs.next() { self.handler.span_err(attr.span(), "the `#[proc_macro_derive]` \ attribute may only be used \ @@ -132,8 +131,6 @@ impl<'a> Visitor<'a> for CollectCustomDerives<'a> { } } - let mut attrs = item.attrs.iter() - .filter(|a| a.check_name("proc_macro_derive")); let attr = match attrs.next() { Some(attr) => attr, None => { @@ -227,7 +224,7 @@ impl<'a> Visitor<'a> for CollectCustomDerives<'a> { Vec::new() }; - if self.in_root { + if self.in_root && item.vis == ast::Visibility::Public { self.derives.push(CustomDerive { span: item.span, trait_name: trait_name, @@ -235,8 +232,12 @@ impl<'a> Visitor<'a> for CollectCustomDerives<'a> { attrs: proc_attrs, }); } else { - let msg = "functions tagged with `#[proc_macro_derive]` must \ - currently reside in the root of the crate"; + let msg = if !self.in_root { + "functions tagged with `#[proc_macro_derive]` must \ + currently reside in the root of the crate" + } else { + "functions tagged with `#[proc_macro_derive]` must be `pub`" + }; self.handler.span_err(item.span, msg); } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 142ae86c6a3..82acbf93488 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -254,10 +254,16 @@ pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>) { Some(Err(msg)) => panic!("{:?}", msg), None => return, }; - match run_tests_console(&opts, tests) { - Ok(true) => {} - Ok(false) => std::process::exit(101), - Err(e) => panic!("io error when running tests: {:?}", e), + if opts.list { + if let Err(e) = list_tests_console(&opts, tests) { + panic!("io error when listing tests: {:?}", e); + } + } else { + match run_tests_console(&opts, tests) { + Ok(true) => {} + Ok(false) => std::process::exit(101), + Err(e) => panic!("io error when running tests: {:?}", e), + } } } @@ -300,7 +306,9 @@ pub enum ColorConfig { } pub struct TestOpts { + pub list: bool, pub filter: Option<String>, + pub filter_exact: bool, pub run_ignored: bool, pub run_tests: bool, pub bench_benchmarks: bool, @@ -316,7 +324,9 @@ impl TestOpts { #[cfg(test)] fn new() -> TestOpts { TestOpts { + list: false, filter: None, + filter_exact: false, run_ignored: false, run_tests: false, bench_benchmarks: false, @@ -338,6 +348,7 @@ fn optgroups() -> Vec<getopts::OptGroup> { vec![getopts::optflag("", "ignored", "Run ignored tests"), getopts::optflag("", "test", "Run tests and not benchmarks"), getopts::optflag("", "bench", "Run benchmarks instead of tests"), + getopts::optflag("", "list", "List all tests and benchmarks"), getopts::optflag("h", "help", "Display this message (longer with --help)"), getopts::optopt("", "logfile", "Write logs to the specified file instead \ of stdout", "PATH"), @@ -348,6 +359,7 @@ fn optgroups() -> Vec<getopts::OptGroup> { getopts::optmulti("", "skip", "Skip tests whose names contain FILTER (this flag can \ be used multiple times)","FILTER"), getopts::optflag("q", "quiet", "Display one character per test instead of one line"), + getopts::optflag("", "exact", "Exactly match filters rather than by substring"), getopts::optopt("", "color", "Configure coloring of output: auto = colorize if stdout is a tty and tests are run on serially (default); always = always colorize output; @@ -407,6 +419,8 @@ pub fn parse_opts(args: &[String]) -> Option<OptRes> { let run_ignored = matches.opt_present("ignored"); let quiet = matches.opt_present("quiet"); + let exact = matches.opt_present("exact"); + let list = matches.opt_present("list"); let logfile = matches.opt_str("logfile"); let logfile = logfile.map(|s| PathBuf::from(&s)); @@ -447,7 +461,9 @@ pub fn parse_opts(args: &[String]) -> Option<OptRes> { }; let test_opts = TestOpts { + list: list, filter: filter, + filter_exact: exact, run_ignored: run_ignored, run_tests: run_tests, bench_benchmarks: bench_benchmarks, @@ -576,7 +592,8 @@ impl<T: Write> ConsoleTestState<T> { } } - pub fn write_plain(&mut self, s: &str) -> io::Result<()> { + pub fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> { + let s = s.as_ref(); match self.out { Pretty(ref mut term) => { term.write_all(s.as_bytes())?; @@ -630,25 +647,28 @@ impl<T: Write> ConsoleTestState<T> { TEST_WARN_TIMEOUT_S)) } - pub fn write_log(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> { + pub fn write_log<S: AsRef<str>>(&mut self, msg: S) -> io::Result<()> { + let msg = msg.as_ref(); match self.log_out { None => Ok(()), - Some(ref mut o) => { - let s = format!("{} {}\n", - match *result { - TrOk => "ok".to_owned(), - TrFailed => "failed".to_owned(), - TrFailedMsg(ref msg) => format!("failed: {}", msg), - TrIgnored => "ignored".to_owned(), - TrMetrics(ref mm) => mm.fmt_metrics(), - TrBench(ref bs) => fmt_bench_samples(bs), - }, - test.name); - o.write_all(s.as_bytes()) - } + Some(ref mut o) => o.write_all(msg.as_bytes()), } } + pub fn write_log_result(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> { + self.write_log( + format!("{} {}\n", + match *result { + TrOk => "ok".to_owned(), + TrFailed => "failed".to_owned(), + TrFailedMsg(ref msg) => format!("failed: {}", msg), + TrIgnored => "ignored".to_owned(), + TrMetrics(ref mm) => mm.fmt_metrics(), + TrBench(ref bs) => fmt_bench_samples(bs), + }, + test.name)) + } + pub fn write_failures(&mut self) -> io::Result<()> { self.write_plain("\nfailures:\n")?; let mut failures = Vec::new(); @@ -741,6 +761,49 @@ pub fn fmt_bench_samples(bs: &BenchSamples) -> String { output } +// List the tests to console, and optionally to logfile. Filters are honored. +pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<()> { + let mut st = ConsoleTestState::new(opts, None::<io::Stdout>)?; + + let mut ntest = 0; + let mut nbench = 0; + let mut nmetric = 0; + + for test in filter_tests(&opts, tests) { + use TestFn::*; + + let TestDescAndFn { desc: TestDesc { name, .. }, testfn } = test; + + let fntype = match testfn { + StaticTestFn(..) | DynTestFn(..) => { ntest += 1; "test" }, + StaticBenchFn(..) | DynBenchFn(..) => { nbench += 1; "benchmark" }, + StaticMetricFn(..) | DynMetricFn(..) => { nmetric += 1; "metric" }, + }; + + st.write_plain(format!("{}: {}\n", name, fntype))?; + st.write_log(format!("{} {}\n", fntype, name))?; + } + + fn plural(count: u32, s: &str) -> String { + match count { + 1 => format!("{} {}", 1, s), + n => format!("{} {}s", n, s), + } + } + + if !opts.quiet { + if ntest != 0 || nbench != 0 || nmetric != 0 { + st.write_plain("\n")?; + } + st.write_plain(format!("{}, {}, {}\n", + plural(ntest, "test"), + plural(nbench, "benchmark"), + plural(nmetric, "metric")))?; + } + + Ok(()) +} + // A simple console test runner pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> { @@ -750,7 +813,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu TeWait(ref test, padding) => st.write_test_start(test, padding), TeTimeout(ref test) => st.write_timeout(test), TeResult(test, result, stdout) => { - st.write_log(&test, &result)?; + st.write_log_result(&test, &result)?; st.write_result(&result)?; match result { TrOk => st.passed += 1, @@ -851,6 +914,11 @@ fn use_color(opts: &TestOpts) -> bool { } } +#[cfg(target_os = "redox")] +fn stdout_isatty() -> bool { + // FIXME: Implement isatty on Redox + false +} #[cfg(unix)] fn stdout_isatty() -> bool { unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 } @@ -1040,6 +1108,12 @@ fn get_concurrency() -> usize { } } + #[cfg(target_os = "redox")] + fn num_cpus() -> usize { + // FIXME: Implement num_cpus on Redox + 1 + } + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", @@ -1118,14 +1192,26 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA None => filtered, Some(ref filter) => { filtered.into_iter() - .filter(|test| test.desc.name.as_slice().contains(&filter[..])) + .filter(|test| { + if opts.filter_exact { + test.desc.name.as_slice() == &filter[..] + } else { + test.desc.name.as_slice().contains(&filter[..]) + } + }) .collect() } }; // Skip tests that match any of the skip filters filtered = filtered.into_iter() - .filter(|t| !opts.skip.iter().any(|sf| t.desc.name.as_slice().contains(&sf[..]))) + .filter(|t| !opts.skip.iter().any(|sf| { + if opts.filter_exact { + t.desc.name.as_slice() == &sf[..] + } else { + t.desc.name.as_slice().contains(&sf[..]) + } + })) .collect(); // Maybe pull out the ignored test and unignore them @@ -1655,6 +1741,77 @@ mod tests { } #[test] + pub fn exact_filter_match() { + fn tests() -> Vec<TestDescAndFn> { + vec!["base", + "base::test", + "base::test1", + "base::test2", + ].into_iter() + .map(|name| TestDescAndFn { + desc: TestDesc { + name: StaticTestName(name), + ignore: false, + should_panic: ShouldPanic::No, + }, + testfn: DynTestFn(Box::new(move |()| {})) + }) + .collect() + } + + let substr = filter_tests(&TestOpts { + filter: Some("base".into()), + ..TestOpts::new() + }, tests()); + assert_eq!(substr.len(), 4); + + let substr = filter_tests(&TestOpts { + filter: Some("bas".into()), + ..TestOpts::new() + }, tests()); + assert_eq!(substr.len(), 4); + + let substr = filter_tests(&TestOpts { + filter: Some("::test".into()), + ..TestOpts::new() + }, tests()); + assert_eq!(substr.len(), 3); + + let substr = filter_tests(&TestOpts { + filter: Some("base::test".into()), + ..TestOpts::new() + }, tests()); + assert_eq!(substr.len(), 3); + + let exact = filter_tests(&TestOpts { + filter: Some("base".into()), + filter_exact: true, ..TestOpts::new() + }, tests()); + assert_eq!(exact.len(), 1); + + let exact = filter_tests(&TestOpts { + filter: Some("bas".into()), + filter_exact: true, + ..TestOpts::new() + }, tests()); + assert_eq!(exact.len(), 0); + + let exact = filter_tests(&TestOpts { + filter: Some("::test".into()), + filter_exact: true, + ..TestOpts::new() + }, tests()); + assert_eq!(exact.len(), 0); + + let exact = filter_tests(&TestOpts { + filter: Some("base::test".into()), + filter_exact: true, + ..TestOpts::new() + }, tests()); + assert_eq!(exact.len(), 1); + } + + #[test] pub fn sort_tests() { let mut opts = TestOpts::new(); opts.run_tests = true; diff --git a/src/llvm b/src/llvm -Subproject d7342a9a957470bb62c890cf88fc655ccfb755c +Subproject ceb177eeefa7d67ca29230d2e7e8584f97d4fda diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index ae2ab932a61..5d5845213e2 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -150,6 +150,8 @@ from_rust(LLVMRustAttribute kind) { return Attribute::UWTable; case ZExt: return Attribute::ZExt; + case InReg: + return Attribute::InReg; default: llvm_unreachable("bad AttributeKind"); } @@ -552,8 +554,13 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateBasicType( uint64_t AlignInBits, unsigned Encoding) { return wrap(Builder->createBasicType( - Name, SizeInBits, - AlignInBits, Encoding)); + Name, + SizeInBits, +#if LLVM_VERSION_LE(3, 9) + AlignInBits, +#endif + Encoding + )); } extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreatePointerType( @@ -645,7 +652,21 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateStaticVariable( LLVMRustMetadataRef Ty, bool isLocalToUnit, LLVMValueRef Val, - LLVMRustMetadataRef Decl = NULL) { + LLVMRustMetadataRef Decl = NULL, + uint64_t AlignInBits = 0) { + Constant *InitVal = cast<Constant>(unwrap(Val)); + +#if LLVM_VERSION_GE(4, 0) + llvm::DIExpression *InitExpr = nullptr; + if (llvm::ConstantInt *IntVal = llvm::dyn_cast<llvm::ConstantInt>(InitVal)) { + InitExpr = Builder->createConstantValueExpression( + IntVal->getValue().getSExtValue()); + } else if (llvm::ConstantFP *FPVal = llvm::dyn_cast<llvm::ConstantFP>(InitVal)) { + InitExpr = Builder->createConstantValueExpression( + FPVal->getValueAPF().bitcastToAPInt().getZExtValue()); + } +#endif + return wrap(Builder->createGlobalVariable(unwrapDI<DIDescriptor>(Context), Name, LinkageName, @@ -653,8 +674,16 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateStaticVariable( LineNo, unwrapDI<DIType>(Ty), isLocalToUnit, - cast<Constant>(unwrap(Val)), - unwrapDIptr<MDNode>(Decl))); +#if LLVM_VERSION_GE(4, 0) + InitExpr, +#else + InitVal, +#endif + unwrapDIptr<MDNode>(Decl) +#if LLVM_VERSION_GE(4, 0) + , AlignInBits +#endif + )); } extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateVariable( @@ -667,14 +696,23 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateVariable( LLVMRustMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags, - unsigned ArgNo) { + unsigned ArgNo, + uint64_t AlignInBits) +{ #if LLVM_VERSION_GE(3, 8) if (Tag == 0x100) { // DW_TAG_auto_variable return wrap(Builder->createAutoVariable( - unwrapDI<DIDescriptor>(Scope), Name, + unwrapDI<DIDescriptor>(Scope), + Name, unwrapDI<DIFile>(File), LineNo, - unwrapDI<DIType>(Ty), AlwaysPreserve, from_rust(Flags))); + unwrapDI<DIType>(Ty), + AlwaysPreserve, + from_rust(Flags) +#if LLVM_VERSION_GE(4,0) + , AlignInBits +#endif + )); } else { return wrap(Builder->createParameterVariable( unwrapDI<DIDescriptor>(Scope), Name, ArgNo, diff --git a/src/rustllvm/llvm-auto-clean-trigger b/src/rustllvm/llvm-auto-clean-trigger index 88dd04b6172..b74f4913858 100644 --- a/src/rustllvm/llvm-auto-clean-trigger +++ b/src/rustllvm/llvm-auto-clean-trigger @@ -1,4 +1,4 @@ # If this file is modified, then llvm will be forcibly cleaned and then rebuilt. # The actual contents of this file do not matter, but to trigger a change on the # build bots then the contents should be changed so git updates the mtime. -2016-12-08 +2016-12-19 diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h index b8c4076f4ce..8f7e0e3d918 100644 --- a/src/rustllvm/rustllvm.h +++ b/src/rustllvm/rustllvm.h @@ -98,6 +98,7 @@ enum LLVMRustAttribute { StructRet = 16, UWTable = 17, ZExt = 18, + InReg = 19, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/src/stage0.txt b/src/stage0.txt index 223974186b1..187c56d7889 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -2,7 +2,7 @@ # compiler itself. For the rustbuild build system, this also describes the # relevant Cargo revision that we're using. # -# Currently Rust always bootstrap from the previous stable release, and in our +# Currently Rust always bootstraps from the previous stable release, and in our # train model this means that the master branch bootstraps from beta, beta # bootstraps from current stable, and stable bootstraps from the previous stable # release. @@ -12,5 +12,5 @@ # tarball for a stable release you'll likely see `1.x.0-$date` where `1.x.0` was # released on `$date` -rustc: beta-2016-11-16 -cargo: nightly-2016-11-16 +rustc: beta-2016-12-16 +cargo: fbeea902d2c9a5be6d99cc35681565d8f7832592 diff --git a/src/test/codegen/fastcall-inreg.rs b/src/test/codegen/fastcall-inreg.rs new file mode 100644 index 00000000000..e1dda4d2be0 --- /dev/null +++ b/src/test/codegen/fastcall-inreg.rs @@ -0,0 +1,85 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Checks if the "fastcall" calling convention marks function arguments +// as "inreg" like the C/C++ compilers for the platforms. +// x86 only. + +// ignore-aarch64 +// ignore-aarch64_be +// ignore-arm +// ignore-armeb +// ignore-avr +// ignore-bpfel +// ignore-bpfeb +// ignore-hexagon +// ignore-mips +// ignore-mipsel +// ignore-mips64 +// ignore-mips64el +// ignore-msp430 +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-powerpc +// ignore-r600 +// ignore-amdgcn +// ignore-sparc +// ignore-sparcv9 +// ignore-sparcel +// ignore-s390x +// ignore-tce +// ignore-thumb +// ignore-thumbeb +// ignore-x86_64 no-ignore-x86 +// ignore-xcore +// ignore-nvptx +// ignore-nvptx64 +// ignore-le32 +// ignore-le64 +// ignore-amdil +// ignore-amdil64 +// ignore-hsail +// ignore-hsail64 +// ignore-spir +// ignore-spir64 +// ignore-kalimba +// ignore-shave +// ignore-wasm32 +// ignore-wasm64 + +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] + +mod tests { + // CHECK: @f1(i32 inreg, i32 inreg, i32) + #[no_mangle] + extern "fastcall" fn f1(_: i32, _: i32, _: i32) {} + + // CHECK: @f2(i32* inreg, i32* inreg, i32*) + #[no_mangle] + extern "fastcall" fn f2(_: *const i32, _: *const i32, _: *const i32) {} + + // CHECK: @f3(float, i32 inreg, i32 inreg, i32) + #[no_mangle] + extern "fastcall" fn f3(_: f32, _: i32, _: i32, _: i32) {} + + // CHECK: @f4(i32 inreg, float, i32 inreg, i32) + #[no_mangle] + extern "fastcall" fn f4(_: i32, _: f32, _: i32, _: i32) {} + + // CHECK: @f5(i64, i32) + #[no_mangle] + extern "fastcall" fn f5(_: i64, _: i32) {} + + // CHECK: @f6(i1 inreg zeroext, i32 inreg, i32) + #[no_mangle] + extern "fastcall" fn f6(_: bool, _: i32, _: i32) {} +} diff --git a/src/test/compile-fail-fulldeps/macro-crate-doesnt-resolve.rs b/src/test/compile-fail-fulldeps/macro-crate-doesnt-resolve.rs index f563a1f88d0..b0a2859a5da 100644 --- a/src/test/compile-fail-fulldeps/macro-crate-doesnt-resolve.rs +++ b/src/test/compile-fail-fulldeps/macro-crate-doesnt-resolve.rs @@ -14,5 +14,5 @@ extern crate macro_crate_test; fn main() { - macro_crate_test::foo(); //~ ERROR unresolved name + macro_crate_test::foo(); //~ ERROR unresolved function `macro_crate_test::foo` } diff --git a/src/test/compile-fail-fulldeps/proc-macro/at-the-root.rs b/src/test/compile-fail-fulldeps/proc-macro/pub-at-crate-root.rs index bb7478d9a5f..2bf25892c44 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/at-the-root.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/pub-at-crate-root.rs @@ -23,3 +23,8 @@ pub mod a { //~ `proc-macro` crate types cannot export any items } } +#[proc_macro_derive(B)] +fn bar(a: proc_macro::TokenStream) -> proc_macro::TokenStream { +//~^ ERROR: functions tagged with `#[proc_macro_derive]` must be `pub` + a +} diff --git a/src/test/compile-fail-fulldeps/proc-macro/signature.rs b/src/test/compile-fail-fulldeps/proc-macro/signature.rs index d9b19d1d85a..b7df9489dff 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/signature.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/signature.rs @@ -15,7 +15,7 @@ extern crate proc_macro; #[proc_macro_derive(A)] -unsafe extern fn foo(a: i32, b: u32) -> u32 { +pub unsafe extern fn foo(a: i32, b: u32) -> u32 { //~^ ERROR: mismatched types //~| NOTE: expected normal fn, found unsafe fn //~| NOTE: expected type `fn(proc_macro::TokenStream) -> proc_macro::TokenStream` diff --git a/src/test/compile-fail-fulldeps/qquote.rs b/src/test/compile-fail-fulldeps/qquote.rs index 8acab3369e4..149985717c6 100644 --- a/src/test/compile-fail-fulldeps/qquote.rs +++ b/src/test/compile-fail-fulldeps/qquote.rs @@ -39,6 +39,6 @@ fn main() { assert_eq!(pprust::expr_to_string(&*quote_expr!(&cx, 23)), "23"); - let expr = quote_expr!(&cx, 2 - $abcd + 7); //~ ERROR unresolved name `abcd` + let expr = quote_expr!(&cx, 2 - $abcd + 7); //~ ERROR unresolved value `abcd` assert_eq!(pprust::expr_to_string(&*expr), "2 - $abcd + 7"); } diff --git a/src/test/compile-fail/E0033.rs b/src/test/compile-fail/E0033.rs index 44f73e10e25..03d47472093 100644 --- a/src/test/compile-fail/E0033.rs +++ b/src/test/compile-fail/E0033.rs @@ -14,8 +14,8 @@ trait SomeTrait { fn main() { let trait_obj: &SomeTrait = SomeTrait; - //~^ ERROR E0425 - //~| NOTE unresolved name + //~^ ERROR expected value, found trait `SomeTrait` + //~| NOTE not a value //~| ERROR E0038 //~| method `foo` has no receiver //~| NOTE the trait `SomeTrait` cannot be made into an object diff --git a/src/test/compile-fail/E0259.rs b/src/test/compile-fail/E0259.rs index 95be48b5ff1..b2129902ef9 100644 --- a/src/test/compile-fail/E0259.rs +++ b/src/test/compile-fail/E0259.rs @@ -15,6 +15,6 @@ extern crate collections; extern crate libc as collections; //~^ ERROR E0259 -//~| NOTE `collections` was already imported +//~| NOTE `collections` already imported fn main() {} diff --git a/src/test/compile-fail/E0277-2.rs b/src/test/compile-fail/E0277-2.rs new file mode 100644 index 00000000000..211c0e6f890 --- /dev/null +++ b/src/test/compile-fail/E0277-2.rs @@ -0,0 +1,34 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { + bar: Bar +} + +struct Bar { + baz: Baz +} + +struct Baz { + x: *const u8 +} + +fn is_send<T: Send>() { } + +fn main() { + is_send::<Foo>(); + //~^ ERROR the trait bound `*const u8: std::marker::Send` is not satisfied in `Foo` + //~| NOTE within `Foo`, the trait `std::marker::Send` is not implemented for `*const u8` + //~| NOTE: `*const u8` cannot be sent between threads safely + //~| NOTE: required because it appears within the type `Baz` + //~| NOTE: required because it appears within the type `Bar` + //~| NOTE: required because it appears within the type `Foo` + //~| NOTE: required by `is_send` +} diff --git a/src/test/compile-fail/E0277.rs b/src/test/compile-fail/E0277.rs index e4cb50cd3f2..e31fea1e458 100644 --- a/src/test/compile-fail/E0277.rs +++ b/src/test/compile-fail/E0277.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::path::Path; + trait Foo { fn bar(&self); } @@ -16,6 +18,13 @@ fn some_func<T: Foo>(foo: T) { foo.bar(); } +fn f(p: Path) { } +//~^ ERROR the trait bound `[u8]: std::marker::Sized` is not satisfied in `std::path::Path` +//~| NOTE within `std::path::Path`, the trait `std::marker::Sized` is not implemented for `[u8]` +//~| NOTE `[u8]` does not have a constant size known at compile-time +//~| NOTE required because it appears within the type `std::path::Path` +//~| NOTE all local variables must have a statically known size + fn main() { some_func(5i32); //~^ ERROR the trait bound `i32: Foo` is not satisfied diff --git a/src/test/compile-fail/E0423.rs b/src/test/compile-fail/E0423.rs index 98b700984a7..f5fea77cf96 100644 --- a/src/test/compile-fail/E0423.rs +++ b/src/test/compile-fail/E0423.rs @@ -12,5 +12,4 @@ fn main () { struct Foo { a: bool }; let f = Foo(); //~ ERROR E0423 - //~^ struct called like a function } diff --git a/src/test/compile-fail/E0424.rs b/src/test/compile-fail/E0424.rs index 911007113d3..445d0c5f3ed 100644 --- a/src/test/compile-fail/E0424.rs +++ b/src/test/compile-fail/E0424.rs @@ -14,10 +14,7 @@ impl Foo { fn bar(self) {} fn foo() { - self.bar(); - //~^ ERROR `self` is not available in a static method [E0424] - //~| NOTE not available in static method - //~| NOTE maybe a `self` argument is missing? + self.bar(); //~ ERROR E0424 } } diff --git a/src/test/compile-fail/E0425.rs b/src/test/compile-fail/E0425.rs index 70f4b1107ad..3786282031f 100644 --- a/src/test/compile-fail/E0425.rs +++ b/src/test/compile-fail/E0425.rs @@ -10,7 +10,7 @@ trait Foo { fn bar() { - Self; //~ ERROR E0425 + elf; //~ ERROR E0425 } } diff --git a/src/test/compile-fail/associated-path-shl.rs b/src/test/compile-fail/associated-path-shl.rs index 6bc110239cd..0295d4248e5 100644 --- a/src/test/compile-fail/associated-path-shl.rs +++ b/src/test/compile-fail/associated-path-shl.rs @@ -11,10 +11,10 @@ // Check that associated paths starting with `<<` are successfully parsed. fn main() { - let _: <<A>::B>::C; //~ ERROR type name `A` is undefined or not in scope - let _ = <<A>::B>::C; //~ ERROR type name `A` is undefined or not in scope - let <<A>::B>::C; //~ ERROR type name `A` is undefined or not in scope - let 0 ... <<A>::B>::C; //~ ERROR type name `A` is undefined or not in scope + let _: <<A>::B>::C; //~ ERROR unresolved type `A` + let _ = <<A>::B>::C; //~ ERROR unresolved type `A` + let <<A>::B>::C; //~ ERROR unresolved type `A` + let 0 ... <<A>::B>::C; //~ ERROR unresolved type `A` //~^ ERROR only char and numeric types are allowed in range patterns - <<A>::B>::C; //~ ERROR type name `A` is undefined or not in scope + <<A>::B>::C; //~ ERROR unresolved type `A` } diff --git a/src/test/compile-fail/associated-types-eq-1.rs b/src/test/compile-fail/associated-types-eq-1.rs index 59d87146097..46d5633c8dd 100644 --- a/src/test/compile-fail/associated-types-eq-1.rs +++ b/src/test/compile-fail/associated-types-eq-1.rs @@ -17,7 +17,7 @@ pub trait Foo { } fn foo2<I: Foo>(x: I) { - let _: A = x.boo(); //~ERROR undefined or not in scope + let _: A = x.boo(); //~ ERROR unresolved type `A` } pub fn main() {} diff --git a/src/test/compile-fail/auxiliary/lint_stability.rs b/src/test/compile-fail/auxiliary/lint_stability.rs index 1049bcd1564..5e3cb606ce0 100644 --- a/src/test/compile-fail/auxiliary/lint_stability.rs +++ b/src/test/compile-fail/auxiliary/lint_stability.rs @@ -132,6 +132,10 @@ pub struct UnstableStruct { pub struct StableStruct { #[stable(feature = "test_feature", since = "1.0.0")] pub i: isize } +#[unstable(feature = "test_feature", issue = "0")] +pub enum UnstableEnum {} +#[stable(feature = "rust1", since = "1.0.0")] +pub enum StableEnum {} #[stable(feature = "test_feature", since = "1.0.0")] #[rustc_deprecated(since = "1.0.0", reason = "text")] diff --git a/src/test/compile-fail/bad-expr-path.rs b/src/test/compile-fail/bad-expr-path.rs index c18a3183477..05400a0eb65 100644 --- a/src/test/compile-fail/bad-expr-path.rs +++ b/src/test/compile-fail/bad-expr-path.rs @@ -8,8 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: unresolved name `m1::arguments` - mod m1 {} -fn main(arguments: Vec<String>) { log(debug, m1::arguments); } +fn main(arguments: Vec<String>) { //~ ERROR main function has wrong type + log(debug, m1::arguments); + //~^ ERROR unresolved function `log` + //~| ERROR unresolved value `debug` + //~| ERROR unresolved value `m1::arguments` +} diff --git a/src/test/compile-fail/bad-expr-path2.rs b/src/test/compile-fail/bad-expr-path2.rs index e1c1afb0049..867166134b2 100644 --- a/src/test/compile-fail/bad-expr-path2.rs +++ b/src/test/compile-fail/bad-expr-path2.rs @@ -8,12 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: unresolved name `m1::arguments` - mod m1 { pub mod arguments {} } -fn main(arguments: Vec<String>) { +fn main(arguments: Vec<String>) { //~ ERROR main function has wrong type log(debug, m1::arguments); + //~^ ERROR unresolved function `log` + //~| ERROR unresolved value `debug` + //~| ERROR expected value, found module `m1::arguments` } diff --git a/src/test/compile-fail/blind-item-item-shadow.rs b/src/test/compile-fail/blind-item-item-shadow.rs index 853282ff014..e9df8868a1e 100644 --- a/src/test/compile-fail/blind-item-item-shadow.rs +++ b/src/test/compile-fail/blind-item-item-shadow.rs @@ -12,6 +12,6 @@ mod foo { pub mod foo { } } //~ NOTE previous definition of `foo` here use foo::foo; //~^ ERROR a module named `foo` has already been defined in this module -//~| was already imported +//~| `foo` already defined fn main() {} diff --git a/src/test/compile-fail/cast-rfc0401.rs b/src/test/compile-fail/cast-rfc0401.rs deleted file mode 100644 index b98f464c902..00000000000 --- a/src/test/compile-fail/cast-rfc0401.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn illegal_cast<U:?Sized,V:?Sized>(u: *const U) -> *const V -{ - u as *const V - //~^ ERROR casting - //~^^ NOTE vtable kinds -} - -fn illegal_cast_2<U:?Sized>(u: *const U) -> *const str -{ - u as *const str - //~^ ERROR casting - //~^^ NOTE vtable kinds -} - -trait Foo { fn foo(&self) {} } -impl<T> Foo for T {} - -trait Bar { fn foo(&self) {} } -impl<T> Bar for T {} - -enum E { - A, B -} - -fn main() -{ - let f: f32 = 1.2; - let v = 0 as *const u8; - let fat_v : *const [u8] = unsafe { &*(0 as *const [u8; 1])}; - let fat_sv : *const [i8] = unsafe { &*(0 as *const [i8; 1])}; - let foo: &Foo = &f; - - let _ = v as &u8; //~ ERROR non-scalar - let _ = v as E; //~ ERROR non-scalar - let _ = v as fn(); //~ ERROR non-scalar - let _ = v as (u32,); //~ ERROR non-scalar - let _ = Some(&v) as *const u8; //~ ERROR non-scalar - - let _ = v as f32; - //~^ ERROR casting - let _ = main as f64; - //~^ ERROR casting - let _ = &v as usize; - //~^ ERROR casting - //~^^ HELP through a raw pointer first - let _ = f as *const u8; - //~^ ERROR casting - let _ = 3_i32 as bool; - //~^ ERROR cannot cast as `bool` [E0054] - //~| unsupported cast - //~| HELP compare with zero - let _ = E::A as bool; - //~^ ERROR cannot cast as `bool` [E0054] - //~| unsupported cast - //~| HELP compare with zero - let _ = 0x61u32 as char; //~ ERROR only `u8` can be cast - - let _ = false as f32; - //~^ ERROR casting - //~^^ HELP through an integer first - let _ = E::A as f32; - //~^ ERROR casting - //~^^ HELP through an integer first - let _ = 'a' as f32; - //~^ ERROR casting - //~^^ HELP through an integer first - - let _ = false as *const u8; - //~^ ERROR casting - let _ = E::A as *const u8; - //~^ ERROR casting - let _ = 'a' as *const u8; - //~^ ERROR casting - - let _ = 42usize as *const [u8]; //~ ERROR casting - let _ = v as *const [u8]; //~ ERROR cannot cast - let _ = fat_v as *const Foo; - //~^ ERROR the trait bound `[u8]: std::marker::Sized` is not satisfied - //~| NOTE the trait `std::marker::Sized` is not implemented for `[u8]` - //~| NOTE `[u8]` does not have a constant size known at compile-time - //~| NOTE required for the cast to the object type `Foo` - let _ = foo as *const str; //~ ERROR casting - let _ = foo as *mut str; //~ ERROR casting - let _ = main as *mut str; //~ ERROR casting - let _ = &f as *mut f32; //~ ERROR casting - let _ = &f as *const f64; //~ ERROR casting - let _ = fat_sv as usize; - //~^ ERROR casting - //~^^ HELP through a thin pointer first - - let a : *const str = "hello"; - let _ = a as *const Foo; - //~^ ERROR the trait bound `str: std::marker::Sized` is not satisfied - //~| NOTE the trait `std::marker::Sized` is not implemented for `str` - //~| NOTE `str` does not have a constant size known at compile-time - //~| NOTE required for the cast to the object type `Foo` - - // check no error cascade - let _ = main.f as *const u32; //~ no field `f` on type `fn() {main}` - - let cf: *const Foo = &0; - let _ = cf as *const [u16]; - //~^ ERROR casting - //~^^ NOTE vtable kinds - let _ = cf as *const Bar; - //~^ ERROR casting - //~^^ NOTE vtable kinds - - vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>(); - //~^ ERROR casting `&{float}` as `f32` is invalid - //~| NOTE cannot cast `&{float}` as `f32` - //~| NOTE did you mean `*s`? -} diff --git a/src/test/compile-fail/class-missing-self.rs b/src/test/compile-fail/class-missing-self.rs index ab76af1cbe6..cab46ec1fbf 100644 --- a/src/test/compile-fail/class-missing-self.rs +++ b/src/test/compile-fail/class-missing-self.rs @@ -16,8 +16,8 @@ impl cat { fn sleep(&self) { loop{} } fn meow(&self) { println!("Meow"); - meows += 1; //~ ERROR unresolved name - sleep(); //~ ERROR unresolved name + meows += 1; //~ ERROR unresolved value `meows` + sleep(); //~ ERROR unresolved function `sleep` } } diff --git a/src/test/compile-fail/coherence-error-suppression.rs b/src/test/compile-fail/coherence-error-suppression.rs index b33f27fbc8a..7c7782b9b44 100644 --- a/src/test/compile-fail/coherence-error-suppression.rs +++ b/src/test/compile-fail/coherence-error-suppression.rs @@ -16,7 +16,7 @@ impl Foo for i8 {} impl Foo for i16 {} impl Foo for i32 {} impl Foo for i64 {} -impl Foo for DoesNotExist {} //~ ERROR `DoesNotExist` is undefined +impl Foo for DoesNotExist {} //~ ERROR unresolved type `DoesNotExist` impl Foo for u8 {} impl Foo for u16 {} impl Foo for u32 {} diff --git a/src/test/compile-fail/derived-errors/issue-31997.rs b/src/test/compile-fail/derived-errors/issue-31997.rs index cf283f6d3e4..2a294a4e31f 100644 --- a/src/test/compile-fail/derived-errors/issue-31997.rs +++ b/src/test/compile-fail/derived-errors/issue-31997.rs @@ -20,7 +20,7 @@ fn closure<F, T>(x: F) -> Result<T, ()> } fn foo() -> Result<(), ()> { - try!(closure(|| bar(0 as *mut _))); //~ ERROR unresolved name `bar` + try!(closure(|| bar(0 as *mut _))); //~ ERROR unresolved function `bar` Ok(()) } diff --git a/src/test/compile-fail/does-nothing.rs b/src/test/compile-fail/does-nothing.rs index c0cd406f062..96e14d2fb22 100644 --- a/src/test/compile-fail/does-nothing.rs +++ b/src/test/compile-fail/does-nothing.rs @@ -8,5 +8,5 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: unresolved name `this_does_nothing_what_the` fn main() { println!("doing"); this_does_nothing_what_the; println!("boing"); } +//~^ ERROR unresolved value `this_does_nothing_what_the` diff --git a/src/test/compile-fail/empty-struct-braces-expr.rs b/src/test/compile-fail/empty-struct-braces-expr.rs index 1c86af30c79..d4e85e9744d 100644 --- a/src/test/compile-fail/empty-struct-braces-expr.rs +++ b/src/test/compile-fail/empty-struct-braces-expr.rs @@ -22,13 +22,13 @@ enum E { } fn main() { - let e1 = Empty1; //~ ERROR `Empty1` is the name of a struct or struct variant - let e1 = Empty1(); //~ ERROR `Empty1` is the name of a struct or struct variant - let e3 = E::Empty3; //~ ERROR `E::Empty3` is the name of a struct or struct variant - let e3 = E::Empty3(); //~ ERROR `E::Empty3` is the name of a struct or struct variant + let e1 = Empty1; //~ ERROR expected value, found struct `Empty1` + let e1 = Empty1(); //~ ERROR expected function, found struct `Empty1` + let e3 = E::Empty3; //~ ERROR expected value, found struct variant `E::Empty3` + let e3 = E::Empty3(); //~ ERROR expected function, found struct variant `E::Empty3` - let xe1 = XEmpty1; //~ ERROR `XEmpty1` is the name of a struct or struct variant - let xe1 = XEmpty1(); //~ ERROR `XEmpty1` is the name of a struct or struct variant + let xe1 = XEmpty1; //~ ERROR expected value, found struct `XEmpty1` + let xe1 = XEmpty1(); //~ ERROR expected function, found struct `XEmpty1` let xe3 = XE::Empty3; //~ ERROR no associated item named `Empty3` found for type let xe3 = XE::Empty3(); //~ ERROR no associated item named `Empty3` found for type } diff --git a/src/test/compile-fail/empty-struct-braces-pat-2.rs b/src/test/compile-fail/empty-struct-braces-pat-2.rs index 4349e72c5d7..d3b13457dc6 100644 --- a/src/test/compile-fail/empty-struct-braces-pat-2.rs +++ b/src/test/compile-fail/empty-struct-braces-pat-2.rs @@ -22,15 +22,15 @@ fn main() { let xe1 = XEmpty1 {}; match e1 { - Empty1() => () //~ ERROR unresolved tuple struct/variant `Empty1` + Empty1() => () //~ ERROR expected tuple struct/variant, found struct `Empty1` } match xe1 { - XEmpty1() => () //~ ERROR unresolved tuple struct/variant `XEmpty1` + XEmpty1() => () //~ ERROR expected tuple struct/variant, found struct `XEmpty1` } match e1 { - Empty1(..) => () //~ ERROR unresolved tuple struct/variant `Empty1` + Empty1(..) => () //~ ERROR expected tuple struct/variant, found struct `Empty1` } match xe1 { - XEmpty1(..) => () //~ ERROR unresolved tuple struct/variant `XEmpty1` + XEmpty1(..) => () //~ ERROR expected tuple struct/variant, found struct `XEmpty1` } } diff --git a/src/test/compile-fail/enum-variant-type-2.rs b/src/test/compile-fail/enum-variant-type-2.rs index eef4bea1df1..258bfd1e3ba 100644 --- a/src/test/compile-fail/enum-variant-type-2.rs +++ b/src/test/compile-fail/enum-variant-type-2.rs @@ -14,6 +14,6 @@ enum Foo { Bar } -fn foo(x: Foo::Bar) {} //~ERROR found value `Foo::Bar` used as a type +fn foo(x: Foo::Bar) {} //~ ERROR expected type, found variant `Foo::Bar` fn main() {} diff --git a/src/test/compile-fail/export-fully-qualified.rs b/src/test/compile-fail/export-fully-qualified.rs index 166ef7ab87f..19fa13f8377 100644 --- a/src/test/compile-fail/export-fully-qualified.rs +++ b/src/test/compile-fail/export-fully-qualified.rs @@ -8,14 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: failed to resolve. Use of undeclared type or module `foo` - // In this test baz isn't resolved when called as foo.baz even though // it's called from inside foo. This is somewhat surprising and may // want to change eventually. mod foo { - pub fn bar() { foo::baz(); } + pub fn bar() { foo::baz(); } //~ ERROR failed to resolve. Use of undeclared type or module `foo` fn baz() { } } diff --git a/src/test/compile-fail/export.rs b/src/test/compile-fail/export.rs index 3a391e7c609..a412cac699f 100644 --- a/src/test/compile-fail/export.rs +++ b/src/test/compile-fail/export.rs @@ -8,10 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: unresolved name mod foo { pub fn x(y: isize) { log(debug, y); } + //~^ ERROR unresolved function `log` + //~| ERROR unresolved value `debug` fn z(y: isize) { log(debug, y); } + //~^ ERROR unresolved function `log` + //~| ERROR unresolved value `debug` } -fn main() { foo::z(10); } +fn main() { foo::z(10); } //~ ERROR function `z` is private diff --git a/src/test/compile-fail/export2.rs b/src/test/compile-fail/export2.rs index f7b1400aa45..dc96ce7f504 100644 --- a/src/test/compile-fail/export2.rs +++ b/src/test/compile-fail/export2.rs @@ -8,10 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: failed to resolve. Use of undeclared type or module `bar` - mod foo { - pub fn x() { bar::x(); } + pub fn x() { bar::x(); } //~ ERROR failed to resolve. Use of undeclared type or module `bar` } mod bar { diff --git a/src/test/compile-fail/extern-with-type-bounds.rs b/src/test/compile-fail/extern-with-type-bounds.rs index d8bdd5974c7..0f8ad8d5388 100644 --- a/src/test/compile-fail/extern-with-type-bounds.rs +++ b/src/test/compile-fail/extern-with-type-bounds.rs @@ -24,7 +24,7 @@ extern "rust-intrinsic" { // Unresolved bounds should still error. fn align_of<T: NoSuchTrait>() -> usize; - //~^ ERROR trait `NoSuchTrait` is not in scope + //~^ ERROR unresolved trait `NoSuchTrait` } fn main() {} diff --git a/src/test/compile-fail/for-expn.rs b/src/test/compile-fail/for-expn.rs index 43776d75a47..a051789ec98 100644 --- a/src/test/compile-fail/for-expn.rs +++ b/src/test/compile-fail/for-expn.rs @@ -13,7 +13,7 @@ fn main() { // Odd formatting to make sure we get the right span. for t in & - foo //~ ERROR unresolved name `foo` + foo //~ ERROR unresolved value `foo` { } } diff --git a/src/test/compile-fail/for-loop-hygiene.rs b/src/test/compile-fail/for-loop-hygiene.rs index f06882875fd..2135ad6e73c 100644 --- a/src/test/compile-fail/for-loop-hygiene.rs +++ b/src/test/compile-fail/for-loop-hygiene.rs @@ -13,6 +13,6 @@ fn main() { for _ in 0..10 { - iter.next(); //~ error: unresolved name `iter` + iter.next(); //~ ERROR unresolved value `iter` } } diff --git a/src/test/compile-fail/glob-resolve1.rs b/src/test/compile-fail/glob-resolve1.rs index 1e5662aa172..58e67796586 100644 --- a/src/test/compile-fail/glob-resolve1.rs +++ b/src/test/compile-fail/glob-resolve1.rs @@ -29,13 +29,13 @@ mod bar { fn foo<T>() {} fn main() { - fpriv(); //~ ERROR: unresolved - epriv(); //~ ERROR: unresolved - B; //~ ERROR: unresolved - C; //~ ERROR: unresolved - import(); //~ ERROR: unresolved - - foo::<A>(); //~ ERROR: not in scope - foo::<C>(); //~ ERROR: not in scope - foo::<D>(); //~ ERROR: not in scope + fpriv(); //~ ERROR unresolved function `fpriv` + epriv(); //~ ERROR unresolved function `epriv` + B; //~ ERROR expected value, found enum `B` + C; //~ ERROR unresolved value `C` + import(); //~ ERROR: unresolved function `import` + + foo::<A>(); //~ ERROR: unresolved type `A` + foo::<C>(); //~ ERROR: unresolved type `C` + foo::<D>(); //~ ERROR: unresolved type `D` } diff --git a/src/test/compile-fail/import-glob-0.rs b/src/test/compile-fail/import-glob-0.rs index 21aa811ea71..12e45cfa2cb 100644 --- a/src/test/compile-fail/import-glob-0.rs +++ b/src/test/compile-fail/import-glob-0.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: unresolved name - use module_of_many_things::*; mod module_of_many_things { @@ -23,6 +21,6 @@ mod module_of_many_things { fn main() { f1(); f2(); - f999(); // 'export' currently doesn't work? + f999(); //~ ERROR unresolved function `f999` f4(); } diff --git a/src/test/compile-fail/imports/rfc-1560-warning-cycle.rs b/src/test/compile-fail/imports/rfc-1560-warning-cycle.rs new file mode 100644 index 00000000000..bed10c87ae1 --- /dev/null +++ b/src/test/compile-fail/imports/rfc-1560-warning-cycle.rs @@ -0,0 +1,30 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] +#![allow(unused)] + +pub struct Foo; + +mod bar { + struct Foo; + + mod baz { + use *; //~ NOTE `Foo` could resolve to the name imported here + use bar::*; //~ NOTE `Foo` could also resolve to the name imported here + fn f(_: Foo) {} + //~^ WARN `Foo` is ambiguous + //~| WARN hard error in a future release + //~| NOTE see issue #38260 + } +} + +#[rustc_error] +fn main() {} //~ ERROR compilation successful diff --git a/src/test/compile-fail/issue-14254.rs b/src/test/compile-fail/issue-14254.rs deleted file mode 100644 index c7bd343bc9a..00000000000 --- a/src/test/compile-fail/issue-14254.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -trait Foo { - fn bar(&self); - fn baz(&self) { } - fn bah(_: Option<&Self>) { } -} - -struct BarTy { - x : isize, - y : f64, -} - -impl BarTy { - fn a() {} - fn b(&self) {} -} - -impl Foo for *const BarTy { - fn bar(&self) { - baz(); - //~^ ERROR: unresolved name `baz` - //~| NOTE did you mean to call `self.baz`? - a; - //~^ ERROR: unresolved name `a` - //~| NOTE unresolved name - } -} - -impl<'a> Foo for &'a BarTy { - fn bar(&self) { - baz(); - //~^ ERROR: unresolved name `baz` - //~| NOTE did you mean to call `self.baz`? - x; - //~^ ERROR: unresolved name `x` - //~| NOTE did you mean `self.x`? - y; - //~^ ERROR: unresolved name `y` - //~| NOTE did you mean `self.y`? - a; - //~^ ERROR: unresolved name `a` - //~| NOTE unresolved name - bah; - //~^ ERROR: unresolved name `bah` - //~| NOTE did you mean to call `Foo::bah`? - b; - //~^ ERROR: unresolved name `b` - //~| NOTE unresolved name - } -} - -impl<'a> Foo for &'a mut BarTy { - fn bar(&self) { - baz(); - //~^ ERROR: unresolved name `baz` - //~| NOTE did you mean to call `self.baz`? - x; - //~^ ERROR: unresolved name `x` - //~| NOTE did you mean `self.x`? - y; - //~^ ERROR: unresolved name `y` - //~| NOTE did you mean `self.y`? - a; - //~^ ERROR: unresolved name `a` - //~| NOTE unresolved name - bah; - //~^ ERROR: unresolved name `bah` - //~| NOTE did you mean to call `Foo::bah`? - b; - //~^ ERROR: unresolved name `b` - //~| NOTE unresolved name - } -} - -impl Foo for Box<BarTy> { - fn bar(&self) { - baz(); - //~^ ERROR: unresolved name `baz` - //~| NOTE did you mean to call `self.baz`? - bah; - //~^ ERROR: unresolved name `bah` - //~| NOTE did you mean to call `Foo::bah`? - } -} - -impl Foo for *const isize { - fn bar(&self) { - baz(); - //~^ ERROR: unresolved name `baz` - //~| NOTE did you mean to call `self.baz`? - bah; - //~^ ERROR: unresolved name `bah` - //~| NOTE did you mean to call `Foo::bah`? - } -} - -impl<'a> Foo for &'a isize { - fn bar(&self) { - baz(); - //~^ ERROR: unresolved name `baz` - //~| NOTE did you mean to call `self.baz`? - bah; - //~^ ERROR: unresolved name `bah` - //~| NOTE did you mean to call `Foo::bah`? - } -} - -impl<'a> Foo for &'a mut isize { - fn bar(&self) { - baz(); - //~^ ERROR: unresolved name `baz` - //~| NOTE did you mean to call `self.baz`? - bah; - //~^ ERROR: unresolved name `bah` - //~| NOTE did you mean to call `Foo::bah`? - } -} - -impl Foo for Box<isize> { - fn bar(&self) { - baz(); - //~^ ERROR: unresolved name `baz` - //~| NOTE did you mean to call `self.baz`? - bah; - //~^ ERROR: unresolved name `bah` - //~| NOTE did you mean to call `Foo::bah`? - } -} diff --git a/src/test/compile-fail/issue-1476.rs b/src/test/compile-fail/issue-1476.rs index 73a0e0c0775..b7797cf5b36 100644 --- a/src/test/compile-fail/issue-1476.rs +++ b/src/test/compile-fail/issue-1476.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - println!("{}", x); //~ ERROR unresolved name `x` + println!("{}", x); //~ ERROR unresolved value `x` } diff --git a/src/test/compile-fail/issue-15167.rs b/src/test/compile-fail/issue-15167.rs index 2bd7da91d2c..4e77636b379 100644 --- a/src/test/compile-fail/issue-15167.rs +++ b/src/test/compile-fail/issue-15167.rs @@ -11,10 +11,10 @@ // macro f should not be able to inject a reference to 'n'. macro_rules! f { () => (n) } -//~^ ERROR unresolved name `n` -//~| ERROR unresolved name `n` -//~| ERROR unresolved name `n` -//~| ERROR unresolved name `n` +//~^ ERROR unresolved value `n` +//~| ERROR unresolved value `n` +//~| ERROR unresolved value `n` +//~| ERROR unresolved value `n` fn main() -> (){ for n in 0..1 { diff --git a/src/test/compile-fail/issue-17546.rs b/src/test/compile-fail/issue-17546.rs index e640ba3f00f..fe125b973d9 100644 --- a/src/test/compile-fail/issue-17546.rs +++ b/src/test/compile-fail/issue-17546.rs @@ -20,7 +20,7 @@ mod foo { } fn new() -> NoResult<MyEnum, String> { - //~^ ERROR: found value `foo::MyEnum::NoResult` used as a type + //~^ ERROR expected type, found variant `NoResult` unimplemented!() } } @@ -30,18 +30,18 @@ mod bar { use foo; fn new() -> Result<foo::MyEnum, String> { - //~^ ERROR: found value `foo::MyEnum::Result` used as a type + //~^ ERROR expected type, found variant `Result` unimplemented!() } } fn new() -> Result<foo::MyEnum, String> { - //~^ ERROR: found value `foo::MyEnum::Result` used as a type + //~^ ERROR expected type, found variant `Result` unimplemented!() } fn newer() -> NoResult<foo::MyEnum, String> { - //~^ ERROR: found value `foo::MyEnum::NoResult` used as a type + //~^ ERROR expected type, found variant `NoResult` unimplemented!() } diff --git a/src/test/compile-fail/issue-18058.rs b/src/test/compile-fail/issue-18058.rs index 0447cf781ff..1611cc418fb 100644 --- a/src/test/compile-fail/issue-18058.rs +++ b/src/test/compile-fail/issue-18058.rs @@ -9,6 +9,6 @@ // except according to those terms. impl Undefined {} -//~^ ERROR type name `Undefined` is undefined or not in scope +//~^ ERROR unresolved type `Undefined` fn main() {} diff --git a/src/test/compile-fail/issue-18119.rs b/src/test/compile-fail/issue-18119.rs index f06496463e4..412f7566f47 100644 --- a/src/test/compile-fail/issue-18119.rs +++ b/src/test/compile-fail/issue-18119.rs @@ -13,10 +13,10 @@ static Y: u8 = 1; fn foo() {} impl X {} -//~^ ERROR type name `X` is undefined or not in scope +//~^ ERROR expected type, found constant `X` impl Y {} -//~^ ERROR type name `Y` is undefined or not in scope +//~^ ERROR expected type, found static `Y` impl foo {} -//~^ ERROR type name `foo` is undefined or not in scope +//~^ ERROR expected type, found function `foo` fn main() {} diff --git a/src/test/compile-fail/issue-19498.rs b/src/test/compile-fail/issue-19498.rs index 2e2115b7110..88e804fb8aa 100644 --- a/src/test/compile-fail/issue-19498.rs +++ b/src/test/compile-fail/issue-19498.rs @@ -11,13 +11,13 @@ use self::A; //~ NOTE previous import of `A` here use self::B; //~ NOTE previous import of `B` here mod A {} //~ ERROR a module named `A` has already been imported in this module -//~| `A` was already imported +//~| `A` already imported pub mod B {} //~ ERROR a module named `B` has already been imported in this module -//~| `B` was already imported +//~| `B` already imported mod C { use C::D; //~ NOTE previous import of `D` here mod D {} //~ ERROR a module named `D` has already been imported in this module - //~| `D` was already imported + //~| `D` already imported } fn main() {} diff --git a/src/test/compile-fail/issue-19883.rs b/src/test/compile-fail/issue-19883.rs index 3a7a1692f38..6fc5fa03c58 100644 --- a/src/test/compile-fail/issue-19883.rs +++ b/src/test/compile-fail/issue-19883.rs @@ -17,7 +17,7 @@ trait From<Src> { trait To: Sized { fn to<Dst: From<Self>>(self) -> <Dst as From<Self>>::Dst - //~^ ERROR associated type `From::Dst` is undefined or not in scope + //~^ ERROR unresolved associated type `From::Dst` { From::from(self) } diff --git a/src/test/compile-fail/issue-22037.rs b/src/test/compile-fail/issue-22037.rs index 74f1be95420..2a81b55dc7b 100644 --- a/src/test/compile-fail/issue-22037.rs +++ b/src/test/compile-fail/issue-22037.rs @@ -11,7 +11,7 @@ trait A { type Output; fn a(&self) -> <Self as A>::X; -//~^ ERROR: associated type `A::X` is undefined or not in scope + //~^ ERROR unresolved associated type `A::X` } impl A for u32 { diff --git a/src/test/compile-fail/issue-22384.rs b/src/test/compile-fail/issue-22384.rs index 46a43bdfcb8..ad42a7e4a97 100644 --- a/src/test/compile-fail/issue-22384.rs +++ b/src/test/compile-fail/issue-22384.rs @@ -14,5 +14,5 @@ trait Trait { fn main() { <<i32 as Copy>::foobar as Trait>::foo(); - //~^ ERROR associated type `Copy::foobar` is undefined or not in scope + //~^ ERROR unresolved associated type `Copy::foobar` } diff --git a/src/test/compile-fail/issue-2281-part1.rs b/src/test/compile-fail/issue-2281-part1.rs index f59252dd315..8d21650ed6f 100644 --- a/src/test/compile-fail/issue-2281-part1.rs +++ b/src/test/compile-fail/issue-2281-part1.rs @@ -8,6 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: unresolved name `foobar` - -fn main() { println!("{}", foobar); } +fn main() { println!("{}", foobar); } //~ ERROR unresolved value `foobar` diff --git a/src/test/compile-fail/issue-2330.rs b/src/test/compile-fail/issue-2330.rs index 63f146a21d9..f1a282695ac 100644 --- a/src/test/compile-fail/issue-2330.rs +++ b/src/test/compile-fail/issue-2330.rs @@ -15,7 +15,7 @@ trait channel<T> { } // `chan` is not a trait, it's an enum -impl chan for isize { //~ ERROR `chan` is not a trait +impl chan for isize { //~ ERROR expected trait, found enum `chan` fn send(&self, v: isize) { panic!() } } diff --git a/src/test/compile-fail/issue-2356.rs b/src/test/compile-fail/issue-2356.rs deleted file mode 100644 index d7635d7bc94..00000000000 --- a/src/test/compile-fail/issue-2356.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -trait Groom { - fn shave(other: usize); -} - -pub struct cat { - whiskers: isize, -} - -pub enum MaybeDog { - Dog, - NoDog -} - -impl MaybeDog { - fn bark() { - // If this provides a suggestion, it's a bug as MaybeDog doesn't impl Groom - shave(); - //~^ ERROR: unresolved name `shave` - //~| NOTE unresolved name - } -} - -impl Groom for cat { - fn shave(other: usize) { - whiskers -= other; - //~^ ERROR: unresolved name `whiskers` - //~| NOTE unresolved name - //~| HELP this is an associated function - shave(4); - //~^ ERROR: unresolved name `shave` - //~| NOTE did you mean to call `Groom::shave`? - purr(); - //~^ ERROR: unresolved name `purr` - //~| NOTE unresolved name - } -} - -impl cat { - fn static_method() {} - - fn purr_louder() { - static_method(); - //~^ ERROR: unresolved name `static_method` - //~| NOTE unresolved name - purr(); - //~^ ERROR: unresolved name `purr` - //~| NOTE unresolved name - purr(); - //~^ ERROR: unresolved name `purr` - //~| NOTE unresolved name - purr(); - //~^ ERROR: unresolved name `purr` - //~| NOTE unresolved name - } -} - -impl cat { - fn meow() { - if self.whiskers > 3 { - //~^ ERROR `self` is not available in a static method [E0424] - //~| NOTE not available in static method - //~| NOTE maybe a `self` argument is missing? - println!("MEOW"); - } - } - - fn purr(&self) { - grow_older(); - //~^ ERROR: unresolved name `grow_older` - //~| NOTE unresolved name - shave(); - //~^ ERROR: unresolved name `shave` - //~| NOTE unresolved name - } - - fn burn_whiskers(&mut self) { - whiskers = 0; - //~^ ERROR: unresolved name `whiskers` - //~| NOTE did you mean `self.whiskers`? - } - - pub fn grow_older(other:usize) { - whiskers = 4; - //~^ ERROR: unresolved name `whiskers` - //~| NOTE unresolved name - //~| HELP this is an associated function - purr_louder(); - //~^ ERROR: unresolved name `purr_louder` - //~| NOTE unresolved name - } -} - -fn main() { - self += 1; - //~^ ERROR: unresolved name `self` - //~| NOTE unresolved name - //~| HELP: module `self` - // it's a bug if this suggests a missing `self` as we're not in a method -} diff --git a/src/test/compile-fail/issue-24081.rs b/src/test/compile-fail/issue-24081.rs index 188716c5e93..26bb72b862f 100644 --- a/src/test/compile-fail/issue-24081.rs +++ b/src/test/compile-fail/issue-24081.rs @@ -15,14 +15,14 @@ use std::ops::Div; //~ NOTE previous import use std::ops::Rem; //~ NOTE previous import type Add = bool; //~ ERROR a trait named `Add` has already been imported in this module -//~| was already imported +//~| `Add` already imported struct Sub { x: f32 } //~ ERROR a trait named `Sub` has already been imported in this module -//~| was already imported +//~| `Sub` already imported enum Mul { A, B } //~ ERROR a trait named `Mul` has already been imported in this module -//~| was already imported +//~| `Mul` already imported mod Div { } //~ ERROR a trait named `Div` has already been imported in this module -//~| was already imported +//~| `Div` already imported trait Rem { } //~ ERROR a trait named `Rem` has already been imported in this module -//~| was already imported +//~| `Rem` already imported fn main() {} diff --git a/src/test/compile-fail/issue-28388-1.rs b/src/test/compile-fail/issue-28388-1.rs index ed7851ec0f1..334fdee00a0 100644 --- a/src/test/compile-fail/issue-28388-1.rs +++ b/src/test/compile-fail/issue-28388-1.rs @@ -10,8 +10,6 @@ // Prefix in imports with empty braces should be resolved and checked privacy, stability, etc. -use foo::{}; -//~^ ERROR failed to resolve. Maybe a missing `extern crate foo;`? -//~| NOTE foo +use foo::{}; //~ ERROR unresolved module or enum `foo` fn main() {} diff --git a/src/test/compile-fail/issue-28388-3.rs b/src/test/compile-fail/issue-28388-3.rs index 4baaa16e772..12357779b51 100644 --- a/src/test/compile-fail/issue-28388-3.rs +++ b/src/test/compile-fail/issue-28388-3.rs @@ -14,8 +14,7 @@ extern crate lint_stability; -use lint_stability::UnstableStruct::{}; -//~^ ERROR use of unstable library feature 'test_feature' -use lint_stability::StableStruct::{}; // OK +use lint_stability::UnstableEnum::{}; //~ ERROR use of unstable library feature 'test_feature' +use lint_stability::StableEnum::{}; // OK fn main() {} diff --git a/src/test/compile-fail/issue-30535.rs b/src/test/compile-fail/issue-30535.rs index 93f3086d057..90f5220a623 100644 --- a/src/test/compile-fail/issue-30535.rs +++ b/src/test/compile-fail/issue-30535.rs @@ -13,7 +13,7 @@ extern crate issue_30535 as foo; fn bar( - _: foo::Foo::FooV //~ ERROR value `foo::Foo::FooV` used as a type + _: foo::Foo::FooV //~ ERROR expected type, found variant `foo::Foo::FooV` ) {} fn main() {} diff --git a/src/test/compile-fail/issue-30589.rs b/src/test/compile-fail/issue-30589.rs index 32765d5acb4..dd5fac9bed1 100644 --- a/src/test/compile-fail/issue-30589.rs +++ b/src/test/compile-fail/issue-30589.rs @@ -10,7 +10,7 @@ use std::fmt; -impl fmt::Display for DecoderError { //~ ERROR E0412 +impl fmt::Display for DecoderError { //~ ERROR unresolved type `DecoderError` fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Missing data: {}", self.0) } diff --git a/src/test/compile-fail/issue-31845.rs b/src/test/compile-fail/issue-31845.rs index 344a1117254..32e004af1f3 100644 --- a/src/test/compile-fail/issue-31845.rs +++ b/src/test/compile-fail/issue-31845.rs @@ -14,7 +14,7 @@ fn f() { fn g() {} mod foo { fn h() { - g(); //~ ERROR unresolved name + g(); //~ ERROR unresolved function `g` } } } diff --git a/src/test/compile-fail/issue-32119.rs b/src/test/compile-fail/issue-32119.rs index 4743b779ef6..e630a01a593 100644 --- a/src/test/compile-fail/issue-32119.rs +++ b/src/test/compile-fail/issue-32119.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(rustc_attrs)] +#![allow(dead_code)] pub type T = (); mod foo { pub use super::T; } diff --git a/src/test/compile-fail/issue-34334.rs b/src/test/compile-fail/issue-34334.rs index ffcd052369d..fa672557c5e 100644 --- a/src/test/compile-fail/issue-34334.rs +++ b/src/test/compile-fail/issue-34334.rs @@ -11,5 +11,5 @@ fn main () { let sr: Vec<(u32, _, _) = vec![]; //~ ERROR expected one of `+`, `,`, or `>`, found `=` let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); - //~^ ERROR unresolved name `sr` + //~^ ERROR unresolved value `sr` } diff --git a/src/test/compile-fail/issue-35075.rs b/src/test/compile-fail/issue-35075.rs index a70452dcbd0..39d06312aa7 100644 --- a/src/test/compile-fail/issue-35075.rs +++ b/src/test/compile-fail/issue-35075.rs @@ -9,11 +9,11 @@ // except according to those terms. struct Bar<T> { - inner: Foo<T> //~ ERROR type name `Foo` is undefined or not in scope + inner: Foo<T> //~ ERROR unresolved type `Foo` } enum Baz<T> { - Foo(Foo<T>) //~ ERROR type name `Foo` is undefined or not in scope + Foo(Foo<T>) //~ ERROR unresolved type `Foo` } fn main() {} diff --git a/src/test/compile-fail/issue-37534.rs b/src/test/compile-fail/issue-37534.rs index eb676601e89..1a6d92166d7 100644 --- a/src/test/compile-fail/issue-37534.rs +++ b/src/test/compile-fail/issue-37534.rs @@ -9,8 +9,8 @@ // except according to those terms. struct Foo<T: ?Hash> { } -//~^ ERROR trait `Hash` is not in scope [E0405] -//~^^ ERROR parameter `T` is never used [E0392] +//~^ ERROR unresolved trait `Hash` +//~^^ ERROR parameter `T` is never used //~^^^ WARN default bound relaxed for a type parameter, but this does nothing fn main() { } diff --git a/src/test/compile-fail/issue-38458.rs b/src/test/compile-fail/issue-38458.rs new file mode 100644 index 00000000000..56eb5f874cd --- /dev/null +++ b/src/test/compile-fail/issue-38458.rs @@ -0,0 +1,15 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const x: () = { + return; //~ ERROR return statement outside of function body +}; + +fn main() {} diff --git a/src/test/compile-fail/issue-4366-2.rs b/src/test/compile-fail/issue-4366-2.rs index a6fe719509c..687720a130c 100644 --- a/src/test/compile-fail/issue-4366-2.rs +++ b/src/test/compile-fail/issue-4366-2.rs @@ -23,7 +23,7 @@ mod a { pub mod sub { use a::b::*; fn sub() -> bar { 1 } - //~^ ERROR: type name `bar` is undefined or not in scope + //~^ ERROR unresolved type `bar` } } @@ -32,5 +32,5 @@ mod m1 { } fn main() { - foo(); //~ ERROR: unresolved name + foo(); //~ ERROR expected function, found module `foo` } diff --git a/src/test/compile-fail/issue-4366.rs b/src/test/compile-fail/issue-4366.rs index 5625ac00c85..18e55ee3c2a 100644 --- a/src/test/compile-fail/issue-4366.rs +++ b/src/test/compile-fail/issue-4366.rs @@ -25,7 +25,7 @@ mod a { } pub mod sub { use a::b::*; - fn sub() -> isize { foo(); 1 } //~ ERROR: unresolved name `foo` + fn sub() -> isize { foo(); 1 } //~ ERROR unresolved function `foo` } } diff --git a/src/test/compile-fail/issue-5099.rs b/src/test/compile-fail/issue-5099.rs index c2e1fc615cc..e78b54bd411 100644 --- a/src/test/compile-fail/issue-5099.rs +++ b/src/test/compile-fail/issue-5099.rs @@ -9,6 +9,6 @@ // except according to those terms. -trait B < A > { fn a() -> A { this.a } } //~ ERROR unresolved name +trait B < A > { fn a() -> A { this.a } } //~ ERROR unresolved value `this` fn main() {} diff --git a/src/test/compile-fail/issue-5927.rs b/src/test/compile-fail/issue-5927.rs index 7668a2117a2..c421dbd1eb3 100644 --- a/src/test/compile-fail/issue-5927.rs +++ b/src/test/compile-fail/issue-5927.rs @@ -12,7 +12,7 @@ fn main() { let z = match 3 { x(1) => x(1) //~ ERROR unresolved tuple struct/variant `x` - //~^ ERROR unresolved name `x` + //~^ ERROR unresolved function `x` }; assert!(z == 3); } diff --git a/src/test/compile-fail/issue-7607-1.rs b/src/test/compile-fail/issue-7607-1.rs index 96ac2de1762..e7b7decbdb0 100644 --- a/src/test/compile-fail/issue-7607-1.rs +++ b/src/test/compile-fail/issue-7607-1.rs @@ -12,7 +12,7 @@ struct Foo { x: isize } -impl Fo { //~ ERROR type name `Fo` is undefined or not in scope +impl Fo { //~ ERROR unresolved type `Fo` fn foo() {} } diff --git a/src/test/compile-fail/issue-8767.rs b/src/test/compile-fail/issue-8767.rs index 1c97c0c886d..318eab92252 100644 --- a/src/test/compile-fail/issue-8767.rs +++ b/src/test/compile-fail/issue-8767.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -impl B { //~ ERROR type name `B` is undefined or not in scope +impl B { //~ ERROR unresolved type `B` } fn main() { diff --git a/src/test/compile-fail/keyword-super-as-identifier.rs b/src/test/compile-fail/keyword-super-as-identifier.rs index 531705563e2..62649ba8a0f 100644 --- a/src/test/compile-fail/keyword-super-as-identifier.rs +++ b/src/test/compile-fail/keyword-super-as-identifier.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let super = "foo"; //~ ERROR unresolved unit struct/variant or constant `super` + let super = "foo"; //~ ERROR failed to resolve. There are too many initial `super`s } diff --git a/src/test/compile-fail/keyword-super.rs b/src/test/compile-fail/keyword-super.rs index 9ac9e800c84..02047bd639f 100644 --- a/src/test/compile-fail/keyword-super.rs +++ b/src/test/compile-fail/keyword-super.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let super: isize; //~ ERROR unresolved unit struct/variant or constant `super` + let super: isize; //~ ERROR failed to resolve. There are too many initial `super`s } diff --git a/src/test/compile-fail/lint-dead-code-type-alias.rs b/src/test/compile-fail/lint-dead-code-type-alias.rs new file mode 100644 index 00000000000..aaa01aa6bbe --- /dev/null +++ b/src/test/compile-fail/lint-dead-code-type-alias.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(dead_code)] + +type Used = u8; +type Unused = u8; //~ ERROR type alias is never used + +fn id(x: Used) -> Used { x } + +fn main() { + id(0); +} diff --git a/src/test/compile-fail/macro-outer-attributes.rs b/src/test/compile-fail/macro-outer-attributes.rs index 0469a9d1cc8..70a50d83904 100644 --- a/src/test/compile-fail/macro-outer-attributes.rs +++ b/src/test/compile-fail/macro-outer-attributes.rs @@ -25,6 +25,6 @@ test!(b, // test1!(#[bar]) #[qux] fn main() { - a::bar(); //~ ERROR unresolved name `a::bar` + a::bar(); //~ ERROR unresolved function `a::bar` b::bar(); } diff --git a/src/test/compile-fail/macro-parameter-span.rs b/src/test/compile-fail/macro-parameter-span.rs index 2ef69759128..dc5a2deab42 100644 --- a/src/test/compile-fail/macro-parameter-span.rs +++ b/src/test/compile-fail/macro-parameter-span.rs @@ -18,6 +18,6 @@ macro_rules! foo { // not to the macro variable '$id' fn main() { foo!( - x //~ ERROR unresolved name `x` + x //~ ERROR unresolved value `x` ); } diff --git a/src/test/compile-fail/macro-tt-matchers.rs b/src/test/compile-fail/macro-tt-matchers.rs index 945490cefb9..969f1500717 100644 --- a/src/test/compile-fail/macro-tt-matchers.rs +++ b/src/test/compile-fail/macro-tt-matchers.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(rustc_attrs)] +#![allow(dead_code)] macro_rules! foo { ($x:tt) => (type Alias = $x<i32>;) diff --git a/src/test/compile-fail/match-join.rs b/src/test/compile-fail/match-join.rs index 4ec426fd3aa..3f6304db957 100644 --- a/src/test/compile-fail/match-join.rs +++ b/src/test/compile-fail/match-join.rs @@ -16,6 +16,6 @@ fn my_panic() -> ! { panic!(); } fn main() { match true { false => { my_panic(); } true => { } } - println!("{}", x); //~ ERROR unresolved name `x` + println!("{}", x); //~ ERROR unresolved value `x` let x: isize; } diff --git a/src/test/compile-fail/match-vec-mismatch.rs b/src/test/compile-fail/match-vec-mismatch.rs index 596cec167c2..4cf8eea78cf 100644 --- a/src/test/compile-fail/match-vec-mismatch.rs +++ b/src/test/compile-fail/match-vec-mismatch.rs @@ -34,7 +34,7 @@ fn main() { [0, 1, 2, 3, x..] => {} //~ ERROR pattern requires }; - match does_not_exist { //~ ERROR unresolved name + match does_not_exist { //~ ERROR unresolved value `does_not_exist` [] => {} }; } diff --git a/src/test/compile-fail/mod_file_correct_spans.rs b/src/test/compile-fail/mod_file_correct_spans.rs index f8ea5dda183..c64b22a7f41 100644 --- a/src/test/compile-fail/mod_file_correct_spans.rs +++ b/src/test/compile-fail/mod_file_correct_spans.rs @@ -13,5 +13,5 @@ mod mod_file_aux; fn main() { - assert!(mod_file_aux::bar() == 10); //~ ERROR unresolved name + assert!(mod_file_aux::bar() == 10); //~ ERROR unresolved function `mod_file_aux::bar` } diff --git a/src/test/compile-fail/name-clash-nullary.rs b/src/test/compile-fail/name-clash-nullary.rs index 4c76c4b8b02..359417aee52 100644 --- a/src/test/compile-fail/name-clash-nullary.rs +++ b/src/test/compile-fail/name-clash-nullary.rs @@ -13,6 +13,6 @@ use std::option::*; fn main() { let None: isize = 42; //~ ERROR let bindings cannot shadow unit variants log(debug, None); - //~^ ERROR unresolved name `debug` - //~| ERROR unresolved name `log` + //~^ ERROR unresolved function `log` + //~| ERROR unresolved value `debug` } diff --git a/src/test/compile-fail/namespace-mix.rs b/src/test/compile-fail/namespace-mix.rs index cb7894b726f..c1c724fc431 100644 --- a/src/test/compile-fail/namespace-mix.rs +++ b/src/test/compile-fail/namespace-mix.rs @@ -41,13 +41,13 @@ mod m2 { fn f12() { check(m1::S{}); //~ ERROR c::Item - check(m1::S); //~ ERROR unresolved name + check(m1::S); //~ ERROR expected value, found type alias `m1::S` check(m2::S{}); //~ ERROR c::S check(m2::S); //~ ERROR c::Item } fn xf12() { check(xm1::S{}); //~ ERROR c::Item - check(xm1::S); //~ ERROR unresolved name + check(xm1::S); //~ ERROR expected value, found type alias `xm1::S` check(xm2::S{}); //~ ERROR c::S check(xm2::S); //~ ERROR c::Item } @@ -107,13 +107,13 @@ mod m8 { fn f78() { check(m7::V{}); //~ ERROR c::Item - check(m7::V); //~ ERROR name of a struct or struct variant + check(m7::V); //~ ERROR expected value, found struct variant `m7::V` check(m8::V{}); //~ ERROR c::E check(m8::V); //~ ERROR c::Item } fn xf78() { check(xm7::V{}); //~ ERROR c::Item - check(xm7::V); //~ ERROR name of a struct or struct variant + check(xm7::V); //~ ERROR expected value, found struct variant `xm7::V` check(xm8::V{}); //~ ERROR c::E check(xm8::V); //~ ERROR c::Item } diff --git a/src/test/compile-fail/namespaced-enum-glob-import-no-impls-xcrate.rs b/src/test/compile-fail/namespaced-enum-glob-import-no-impls-xcrate.rs index 4fcb31d3686..d92323e290b 100644 --- a/src/test/compile-fail/namespaced-enum-glob-import-no-impls-xcrate.rs +++ b/src/test/compile-fail/namespaced-enum-glob-import-no-impls-xcrate.rs @@ -18,8 +18,8 @@ mod m { pub fn main() { use namespaced_enums::Foo::*; - foo(); //~ ERROR unresolved name `foo` - m::foo(); //~ ERROR unresolved name `m::foo` - bar(); //~ ERROR unresolved name `bar` - m::bar(); //~ ERROR unresolved name `m::bar` + foo(); //~ ERROR unresolved function `foo` + m::foo(); //~ ERROR unresolved function `m::foo` + bar(); //~ ERROR unresolved function `bar` + m::bar(); //~ ERROR unresolved function `m::bar` } diff --git a/src/test/compile-fail/namespaced-enum-glob-import-no-impls.rs b/src/test/compile-fail/namespaced-enum-glob-import-no-impls.rs index 4437482fb67..b7c7397ee98 100644 --- a/src/test/compile-fail/namespaced-enum-glob-import-no-impls.rs +++ b/src/test/compile-fail/namespaced-enum-glob-import-no-impls.rs @@ -28,8 +28,8 @@ mod m { pub fn main() { use m2::Foo::*; - foo(); //~ ERROR unresolved name `foo` - m::foo(); //~ ERROR unresolved name `m::foo` - bar(); //~ ERROR unresolved name `bar` - m::bar(); //~ ERROR unresolved name `m::bar` + foo(); //~ ERROR unresolved function `foo` + m::foo(); //~ ERROR unresolved function `m::foo` + bar(); //~ ERROR unresolved function `bar` + m::bar(); //~ ERROR unresolved function `m::bar` } diff --git a/src/test/compile-fail/nested-cfg-attrs.rs b/src/test/compile-fail/nested-cfg-attrs.rs index 6010b1e695e..f3e20f4f614 100644 --- a/src/test/compile-fail/nested-cfg-attrs.rs +++ b/src/test/compile-fail/nested-cfg-attrs.rs @@ -11,4 +11,4 @@ #[cfg_attr(all(), cfg_attr(all(), cfg(foo)))] fn f() {} -fn main() { f() } //~ ERROR unresolved name `f` +fn main() { f() } //~ ERROR unresolved function `f` diff --git a/src/test/compile-fail/no-implicit-prelude-nested.rs b/src/test/compile-fail/no-implicit-prelude-nested.rs index af1046bcd5d..49e2e9f34fa 100644 --- a/src/test/compile-fail/no-implicit-prelude-nested.rs +++ b/src/test/compile-fail/no-implicit-prelude-nested.rs @@ -18,26 +18,26 @@ mod foo { mod baz { struct Test; - impl Add for Test {} //~ ERROR: not in scope - impl Clone for Test {} //~ ERROR: not in scope - impl Iterator for Test {} //~ ERROR: not in scope - impl ToString for Test {} //~ ERROR: not in scope - impl Writer for Test {} //~ ERROR: not in scope + impl Add for Test {} //~ ERROR unresolved trait `Add` + impl Clone for Test {} //~ ERROR unresolved trait `Clone` + impl Iterator for Test {} //~ ERROR unresolved trait `Iterator` + impl ToString for Test {} //~ ERROR unresolved trait `ToString` + impl Writer for Test {} //~ ERROR unresolved trait `Writer` fn foo() { - drop(2) //~ ERROR: unresolved name + drop(2) //~ ERROR unresolved function `drop` } } struct Test; - impl Add for Test {} //~ ERROR: not in scope - impl Clone for Test {} //~ ERROR: not in scope - impl Iterator for Test {} //~ ERROR: not in scope - impl ToString for Test {} //~ ERROR: not in scope - impl Writer for Test {} //~ ERROR: not in scope + impl Add for Test {} //~ ERROR unresolved trait `Add` + impl Clone for Test {} //~ ERROR unresolved trait `Clone` + impl Iterator for Test {} //~ ERROR unresolved trait `Iterator` + impl ToString for Test {} //~ ERROR unresolved trait `ToString` + impl Writer for Test {} //~ ERROR unresolved trait `Writer` fn foo() { - drop(2) //~ ERROR: unresolved name + drop(2) //~ ERROR unresolved function `drop` } } @@ -45,14 +45,14 @@ fn qux() { #[no_implicit_prelude] mod qux_inner { struct Test; - impl Add for Test {} //~ ERROR: not in scope - impl Clone for Test {} //~ ERROR: not in scope - impl Iterator for Test {} //~ ERROR: not in scope - impl ToString for Test {} //~ ERROR: not in scope - impl Writer for Test {} //~ ERROR: not in scope + impl Add for Test {} //~ ERROR unresolved trait `Add` + impl Clone for Test {} //~ ERROR unresolved trait `Clone` + impl Iterator for Test {} //~ ERROR unresolved trait `Iterator` + impl ToString for Test {} //~ ERROR unresolved trait `ToString` + impl Writer for Test {} //~ ERROR unresolved trait `Writer` fn foo() { - drop(2) //~ ERROR: unresolved name + drop(2) //~ ERROR unresolved function `drop` } } } diff --git a/src/test/compile-fail/no-implicit-prelude.rs b/src/test/compile-fail/no-implicit-prelude.rs index 4693fd14e7d..b830a64fa81 100644 --- a/src/test/compile-fail/no-implicit-prelude.rs +++ b/src/test/compile-fail/no-implicit-prelude.rs @@ -17,12 +17,12 @@ // fail with the same error message). struct Test; -impl Add for Test {} //~ ERROR: not in scope -impl Clone for Test {} //~ ERROR: not in scope -impl Iterator for Test {} //~ ERROR: not in scope -impl ToString for Test {} //~ ERROR: not in scope -impl Writer for Test {} //~ ERROR: not in scope +impl Add for Test {} //~ ERROR unresolved trait `Add` +impl Clone for Test {} //~ ERROR unresolved trait `Clone` +impl Iterator for Test {} //~ ERROR unresolved trait `Iterator` +impl ToString for Test {} //~ ERROR unresolved trait `ToString` +impl Writer for Test {} //~ ERROR unresolved trait `Writer` fn main() { - drop(2) //~ ERROR: unresolved name + drop(2) //~ ERROR unresolved function `drop` } diff --git a/src/test/compile-fail/no-link.rs b/src/test/compile-fail/no-link.rs index c4737a37399..7e4e55543cd 100644 --- a/src/test/compile-fail/no-link.rs +++ b/src/test/compile-fail/no-link.rs @@ -15,5 +15,5 @@ extern crate empty_struct; //~^ WARN custom derive crates and `#[no_link]` crates have no effect without `#[macro_use]` fn main() { - empty_struct::XEmpty1; //~ ERROR unresolved name + empty_struct::XEmpty1; //~ ERROR unresolved value `empty_struct::XEmpty1` } diff --git a/src/test/compile-fail/parser-recovery-1.rs b/src/test/compile-fail/parser-recovery-1.rs index 85b62461238..373b33c3e49 100644 --- a/src/test/compile-fail/parser-recovery-1.rs +++ b/src/test/compile-fail/parser-recovery-1.rs @@ -14,11 +14,11 @@ trait Foo { fn bar() { - let x = foo(); //~ ERROR unresolved name `foo` + let x = foo(); //~ ERROR unresolved function `foo` } fn main() { let x = y.; //~ ERROR unexpected token - //~^ ERROR unresolved name `y` + //~^ ERROR unresolved value `y` } //~ ERROR this file contains an un-closed delimiter diff --git a/src/test/compile-fail/parser-recovery-2.rs b/src/test/compile-fail/parser-recovery-2.rs index 109da6251e3..c2bbbda4011 100644 --- a/src/test/compile-fail/parser-recovery-2.rs +++ b/src/test/compile-fail/parser-recovery-2.rs @@ -14,11 +14,11 @@ trait Foo { fn bar() { - let x = foo(); //~ ERROR unresolved name `foo` + let x = foo(); //~ ERROR unresolved function `foo` ) //~ ERROR incorrect close delimiter: `)` } fn main() { let x = y.; //~ ERROR unexpected token - //~^ ERROR unresolved name `y` + //~^ ERROR unresolved value `y` } diff --git a/src/test/compile-fail/pattern-macro-hygiene.rs b/src/test/compile-fail/pattern-macro-hygiene.rs index 1c79c9a2293..24f29666172 100644 --- a/src/test/compile-fail/pattern-macro-hygiene.rs +++ b/src/test/compile-fail/pattern-macro-hygiene.rs @@ -12,5 +12,5 @@ macro_rules! foo { () => ( x ) } fn main() { let foo!() = 2; - x + 1; //~ ERROR unresolved name `x` + x + 1; //~ ERROR unresolved value `x` } diff --git a/src/test/compile-fail/privacy-ns1.rs b/src/test/compile-fail/privacy-ns1.rs index dcab3a46b0a..9c1e8250dbc 100644 --- a/src/test/compile-fail/privacy-ns1.rs +++ b/src/test/compile-fail/privacy-ns1.rs @@ -27,7 +27,7 @@ pub mod foo1 { fn test_glob1() { use foo1::*; - Bar(); //~ ERROR unresolved name `Bar` + Bar(); //~ ERROR expected function, found trait `Bar` } // private type, public value @@ -42,7 +42,7 @@ pub mod foo2 { fn test_glob2() { use foo2::*; - let _x: Box<Bar>; //~ ERROR type name `Bar` is undefined or not in scope + let _x: Box<Bar>; //~ ERROR expected type, found function `Bar` } // neither public @@ -57,8 +57,8 @@ pub mod foo3 { fn test_glob3() { use foo3::*; - Bar(); //~ ERROR unresolved name `Bar` - let _x: Box<Bar>; //~ ERROR type name `Bar` is undefined or not in scope + Bar(); //~ ERROR unresolved function `Bar` + let _x: Box<Bar>; //~ ERROR unresolved type `Bar` } fn main() { diff --git a/src/test/compile-fail/privacy-ns2.rs b/src/test/compile-fail/privacy-ns2.rs index 7accf0ca820..ec9396b5e7b 100644 --- a/src/test/compile-fail/privacy-ns2.rs +++ b/src/test/compile-fail/privacy-ns2.rs @@ -27,13 +27,13 @@ pub mod foo1 { fn test_single1() { use foo1::Bar; - Bar(); //~ ERROR unresolved name `Bar` + Bar(); //~ ERROR expected function, found trait `Bar` } fn test_list1() { use foo1::{Bar,Baz}; - Bar(); //~ ERROR unresolved name `Bar` + Bar(); //~ ERROR expected function, found trait `Bar` } // private type, public value @@ -48,13 +48,13 @@ pub mod foo2 { fn test_single2() { use foo2::Bar; - let _x : Box<Bar>; //~ ERROR type name `Bar` is undefined + let _x : Box<Bar>; //~ ERROR expected type, found function `Bar` } fn test_list2() { use foo2::{Bar,Baz}; - let _x: Box<Bar>; //~ ERROR type name `Bar` is undefined + let _x: Box<Bar>; //~ ERROR expected type, found function `Bar` } // neither public diff --git a/src/test/compile-fail/privacy/restricted/test.rs b/src/test/compile-fail/privacy/restricted/test.rs index 3e1bb766622..01e2c6cd7e8 100644 --- a/src/test/compile-fail/privacy/restricted/test.rs +++ b/src/test/compile-fail/privacy/restricted/test.rs @@ -57,6 +57,6 @@ fn main() { } mod pathological { - pub(bad::path) mod m1 {} //~ ERROR failed to resolve module path + pub(bad::path) mod m1 {} //~ ERROR failed to resolve. Maybe a missing `extern crate bad;`? pub(foo) mod m2 {} //~ ERROR visibilities can only be restricted to ancestor modules } diff --git a/src/test/compile-fail/privacy/restricted/ty-params.rs b/src/test/compile-fail/privacy/restricted/ty-params.rs index ae60c4366ee..593713a6e05 100644 --- a/src/test/compile-fail/privacy/restricted/ty-params.rs +++ b/src/test/compile-fail/privacy/restricted/ty-params.rs @@ -16,11 +16,11 @@ macro_rules! m { struct S<T>(T); m!{ S<u8> } //~ ERROR type or lifetime parameters in visibility path -//~^ ERROR failed to resolve module path. Not a module `S` +//~^ ERROR expected module, found struct `S` mod foo { struct S(pub(foo<T>) ()); //~ ERROR type or lifetime parameters in visibility path - //~^ ERROR type name `T` is undefined or not in scope + //~^ ERROR unresolved type `T` } fn main() {} diff --git a/src/test/compile-fail/recursive-reexports.rs b/src/test/compile-fail/recursive-reexports.rs index 6fd52beeec6..48ec16a7610 100644 --- a/src/test/compile-fail/recursive-reexports.rs +++ b/src/test/compile-fail/recursive-reexports.rs @@ -10,6 +10,8 @@ // aux-build:recursive_reexports.rs -fn f() -> recursive_reexports::S {} //~ ERROR undeclared +extern crate recursive_reexports; + +fn f() -> recursive_reexports::S {} //~ ERROR unresolved type `recursive_reexports::S` fn main() {} diff --git a/src/test/compile-fail/resolve-bad-import-prefix.rs b/src/test/compile-fail/resolve-bad-import-prefix.rs new file mode 100644 index 00000000000..6b4a5122ad0 --- /dev/null +++ b/src/test/compile-fail/resolve-bad-import-prefix.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod m {} +enum E {} +struct S; +trait Tr {} + +use {}; // OK +use ::{}; // OK +use m::{}; // OK +use E::{}; // OK +use S::{}; //~ ERROR expected module or enum, found struct `S` +use Tr::{}; //~ ERROR expected module or enum, found trait `Tr` +use Nonexistent::{}; //~ ERROR unresolved module or enum `Nonexistent` + +fn main () {} diff --git a/src/test/compile-fail/resolve-bad-visibility.rs b/src/test/compile-fail/resolve-bad-visibility.rs new file mode 100644 index 00000000000..088a4e6cd76 --- /dev/null +++ b/src/test/compile-fail/resolve-bad-visibility.rs @@ -0,0 +1,27 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(pub_restricted)] + +enum E {} +trait Tr {} + +pub(E) struct S; //~ ERROR expected module, found enum `E` +pub(Tr) struct Z; //~ ERROR expected module, found trait `Tr` +pub(std::vec) struct F; //~ ERROR visibilities can only be restricted to ancestor modules +pub(nonexistent) struct G; //~ ERROR unresolved module `nonexistent` +pub(too_soon) struct H; //~ ERROR unresolved module `too_soon` + +// Visibilities are resolved eagerly without waiting for modules becoming fully populated. +// Visibilities can only use ancestor modules legally which are always available in time, +// so the worst thing that can happen due to eager resolution is a suboptimal error message. +mod too_soon {} + +fn main () {} diff --git a/src/test/compile-fail/resolve-conflict-item-vs-import.rs b/src/test/compile-fail/resolve-conflict-item-vs-import.rs index 5a068ce4214..2083d98e09d 100644 --- a/src/test/compile-fail/resolve-conflict-item-vs-import.rs +++ b/src/test/compile-fail/resolve-conflict-item-vs-import.rs @@ -13,6 +13,6 @@ use std::mem::transmute; fn transmute() {} //~^ ERROR a value named `transmute` has already been imported in this module -//~| was already imported +//~| `transmute` already imported fn main() { } diff --git a/src/test/compile-fail/resolve-primitive-fallback.rs b/src/test/compile-fail/resolve-primitive-fallback.rs new file mode 100644 index 00000000000..de463cd9e6a --- /dev/null +++ b/src/test/compile-fail/resolve-primitive-fallback.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + // Make sure primitive type fallback doesn't work in value namespace + std::mem::size_of(u16); + //~^ ERROR expected value, found builtin type `u16` + //~| ERROR this function takes 0 parameters but 1 parameter was supplied + + // Make sure primitive type fallback doesn't work with global paths + let _: ::u8; + //~^ ERROR unresolved type `u8` +} diff --git a/src/test/compile-fail/resolve-unknown-trait.rs b/src/test/compile-fail/resolve-unknown-trait.rs index dae3a79832b..affafbfadcf 100644 --- a/src/test/compile-fail/resolve-unknown-trait.rs +++ b/src/test/compile-fail/resolve-unknown-trait.rs @@ -10,10 +10,10 @@ trait NewTrait : SomeNonExistentTrait {} -//~^ ERROR trait `SomeNonExistentTrait` is not in scope +//~^ ERROR unresolved trait `SomeNonExistentTrait` impl SomeNonExistentTrait for isize {} -//~^ ERROR trait `SomeNonExistentTrait` is not in scope +//~^ ERROR unresolved trait `SomeNonExistentTrait` fn f<T:SomeNonExistentTrait>() {} -//~^ ERROR trait `SomeNonExistentTrait` is not in scope +//~^ ERROR unresolved trait `SomeNonExistentTrait` diff --git a/src/test/compile-fail/rmeta.rs b/src/test/compile-fail/rmeta.rs index e81e0541096..455574bbb9d 100644 --- a/src/test/compile-fail/rmeta.rs +++ b/src/test/compile-fail/rmeta.rs @@ -15,5 +15,5 @@ #![crate_type="metadata"] fn main() { - let _ = Foo; //~ ERROR unresolved name `Foo` + let _ = Foo; //~ ERROR unresolved value `Foo` } diff --git a/src/test/compile-fail/struct-fields-shorthand-unresolved.rs b/src/test/compile-fail/struct-fields-shorthand-unresolved.rs index 50a43f4a276..d1555373015 100644 --- a/src/test/compile-fail/struct-fields-shorthand-unresolved.rs +++ b/src/test/compile-fail/struct-fields-shorthand-unresolved.rs @@ -19,6 +19,6 @@ fn main() { let x = 0; let foo = Foo { x, - y //~ ERROR unresolved name `y` + y //~ ERROR unresolved value `y` }; } diff --git a/src/test/compile-fail/syntax-extension-minor.rs b/src/test/compile-fail/syntax-extension-minor.rs index f06e3544e57..0beb4f084c8 100644 --- a/src/test/compile-fail/syntax-extension-minor.rs +++ b/src/test/compile-fail/syntax-extension-minor.rs @@ -18,7 +18,7 @@ pub fn main() { // this now fails (correctly, I claim) because hygiene prevents // the assembled identifier from being a reference to the binding. assert!(concat_idents!(asd, f_f, dsa) == "<.<".to_string()); - //~^ ERROR: unresolved name `asdf_fdsa` + //~^ ERROR unresolved value `asdf_fdsa` assert_eq!(stringify!(use_mention_distinction), "use_mention_distinction"); } diff --git a/src/test/compile-fail/test-cfg.rs b/src/test/compile-fail/test-cfg.rs index 0709d909512..28c69e8df22 100644 --- a/src/test/compile-fail/test-cfg.rs +++ b/src/test/compile-fail/test-cfg.rs @@ -14,5 +14,5 @@ fn foo() {} fn main() { - foo(); //~ ERROR unresolved name `foo` + foo(); //~ ERROR unresolved function `foo` } diff --git a/src/test/compile-fail/ufcs-partially-resolved.rs b/src/test/compile-fail/ufcs-partially-resolved.rs new file mode 100644 index 00000000000..5337272343b --- /dev/null +++ b/src/test/compile-fail/ufcs-partially-resolved.rs @@ -0,0 +1,66 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(associated_type_defaults)] + +trait Tr { + type Y = u16; + fn Y() {} +} +impl Tr for u8 {} + +trait Dr { + type X = u16; + fn Z() {} +} +impl Dr for u8 {} + +enum E { Y } +type A = u32; + +fn main() { + let _: <u8 as Tr>::N; //~ ERROR unresolved associated type `Tr::N` + let _: <u8 as E>::N; //~ ERROR unresolved associated type `E::N` + let _: <u8 as A>::N; //~ ERROR unresolved associated type `A::N` + <u8 as Tr>::N; //~ ERROR unresolved method or associated constant `Tr::N` + <u8 as E>::N; //~ ERROR unresolved method or associated constant `E::N` + <u8 as A>::N; //~ ERROR unresolved method or associated constant `A::N` + let _: <u8 as Tr>::Y; // OK + let _: <u8 as E>::Y; //~ ERROR expected associated type, found variant `E::Y` + <u8 as Tr>::Y; // OK + <u8 as E>::Y; //~ ERROR expected method or associated constant, found unit variant `E::Y` + + let _: <u8 as Tr>::N::NN; //~ ERROR unresolved associated type `Tr::N` + let _: <u8 as E>::N::NN; //~ ERROR unresolved associated type `E::N` + let _: <u8 as A>::N::NN; //~ ERROR unresolved associated type `A::N` + <u8 as Tr>::N::NN; //~ ERROR unresolved associated type `Tr::N` + <u8 as E>::N::NN; //~ ERROR unresolved associated type `E::N` + <u8 as A>::N::NN; //~ ERROR unresolved associated type `A::N` + let _: <u8 as Tr>::Y::NN; //~ ERROR ambiguous associated type + let _: <u8 as E>::Y::NN; //~ ERROR expected associated type, found variant `E::Y` + <u8 as Tr>::Y::NN; //~ ERROR no associated item named `NN` found for type `<u8 as Tr>::Y` + <u8 as E>::Y::NN; //~ ERROR expected associated type, found variant `E::Y` + + let _: <u8 as Tr::N>::NN; //~ ERROR unresolved associated type `Tr::N::NN` + let _: <u8 as E::N>::NN; //~ ERROR unresolved associated type `E::N::NN` + let _: <u8 as A::N>::NN; //~ ERROR unresolved associated type `A::N::NN` + <u8 as Tr::N>::NN; //~ ERROR unresolved method or associated constant `Tr::N::NN` + <u8 as E::N>::NN; //~ ERROR unresolved method or associated constant `E::N::NN` + <u8 as A::N>::NN; //~ ERROR unresolved method or associated constant `A::N::NN` + let _: <u8 as Tr::Y>::NN; //~ ERROR unresolved associated type `Tr::Y::NN` + let _: <u8 as E::Y>::NN; //~ ERROR unresolved associated type `E::Y::NN` + <u8 as Tr::Y>::NN; //~ ERROR unresolved method or associated constant `Tr::Y::NN` + <u8 as E::Y>::NN; //~ ERROR unresolved method or associated constant `E::Y::NN` + + let _: <u8 as Dr>::Z; //~ ERROR expected associated type, found method `Dr::Z` + <u8 as Dr>::X; //~ ERROR expected method or associated constant, found associated type `Dr::X` + let _: <u8 as Dr>::Z::N; //~ ERROR expected associated type, found method `Dr::Z` + <u8 as Dr>::X::N; //~ ERROR no associated item named `N` found for type `<u8 as Dr>::X` +} diff --git a/src/test/compile-fail/variant-used-as-type.rs b/src/test/compile-fail/variant-used-as-type.rs index 73defa6eef9..c889b7d28f8 100644 --- a/src/test/compile-fail/variant-used-as-type.rs +++ b/src/test/compile-fail/variant-used-as-type.rs @@ -15,7 +15,7 @@ enum Ty { A, B(Ty::A), - //~^ ERROR: found value `Ty::A` used as a type + //~^ ERROR expected type, found variant `Ty::A` } @@ -25,6 +25,6 @@ enum E { } impl E::A {} -//~^ ERROR: found value `E::A` used as a type +//~^ ERROR expected type, found variant `E::A` fn main() {} diff --git a/src/test/compile-fail/xcrate-unit-struct.rs b/src/test/compile-fail/xcrate-unit-struct.rs index 214a2a371ba..04af7133000 100644 --- a/src/test/compile-fail/xcrate-unit-struct.rs +++ b/src/test/compile-fail/xcrate-unit-struct.rs @@ -17,6 +17,6 @@ extern crate xcrate_unit_struct; fn main() { let _ = xcrate_unit_struct::StructWithFields; - //~^ ERROR: `xcrate_unit_struct::StructWithFields` is the name of a struct or struct variant + //~^ ERROR expected value, found struct `xcrate_unit_struct::StructWithFields` let _ = xcrate_unit_struct::Struct; } diff --git a/src/test/debuginfo/borrowed-enum.rs b/src/test/debuginfo/borrowed-enum.rs index ddc29c64302..f34fc3b20d2 100644 --- a/src/test/debuginfo/borrowed-enum.rs +++ b/src/test/debuginfo/borrowed-enum.rs @@ -18,11 +18,11 @@ // gdb-command:run // gdb-command:print *the_a_ref -// gdbg-check:$1 = {{RUST$ENUM$DISR = TheA, x = 0, y = 8970181431921507452}, {RUST$ENUM$DISR = TheA, __0 = 0, __1 = 2088533116, __2 = 2088533116}} +// gdbg-check:$1 = {{RUST$ENUM$DISR = TheA, x = 0, y = 8970181431921507452}, {RUST$ENUM$DISR = TheA, [...]}} // gdbr-check:$1 = borrowed_enum::ABC::TheA{x: 0, y: 8970181431921507452} // gdb-command:print *the_b_ref -// gdbg-check:$2 = {{RUST$ENUM$DISR = TheB, x = 0, y = 1229782938247303441}, {RUST$ENUM$DISR = TheB, __0 = 0, __1 = 286331153, __2 = 286331153}} +// gdbg-check:$2 = {{RUST$ENUM$DISR = TheB, [...]}, {RUST$ENUM$DISR = TheB, __0 = 0, __1 = 286331153, __2 = 286331153}} // gdbr-check:$2 = borrowed_enum::ABC::TheB(0, 286331153, 286331153) // gdb-command:print *univariant_ref diff --git a/src/test/debuginfo/by-value-non-immediate-argument.rs b/src/test/debuginfo/by-value-non-immediate-argument.rs index 6d821dbc155..0fe08c3a227 100644 --- a/src/test/debuginfo/by-value-non-immediate-argument.rs +++ b/src/test/debuginfo/by-value-non-immediate-argument.rs @@ -42,7 +42,7 @@ // gdb-command:continue // gdb-command:print x -// gdbg-check:$7 = {{RUST$ENUM$DISR = Case1, x = 0, y = 8970181431921507452}, {RUST$ENUM$DISR = Case1, __0 = 0, __1 = 2088533116, __2 = 2088533116}} +// gdbg-check:$7 = {{RUST$ENUM$DISR = Case1, x = 0, y = 8970181431921507452}, {RUST$ENUM$DISR = Case1, [...]}} // gdbr-check:$7 = by_value_non_immediate_argument::Enum::Case1{x: 0, y: 8970181431921507452} // gdb-command:continue diff --git a/src/test/debuginfo/generic-struct-style-enum.rs b/src/test/debuginfo/generic-struct-style-enum.rs index dba9422721a..a328eec6893 100644 --- a/src/test/debuginfo/generic-struct-style-enum.rs +++ b/src/test/debuginfo/generic-struct-style-enum.rs @@ -17,15 +17,15 @@ // gdb-command:run // gdb-command:print case1 -// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, a = 0, b = 31868, c = 31868, d = 31868, e = 31868}, {RUST$ENUM$DISR = Case1, a = 0, b = 2088533116, c = 2088533116}, {RUST$ENUM$DISR = Case1, a = 0, b = 8970181431921507452}} +// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, a = 0, b = 31868, c = 31868, d = 31868, e = 31868}, {RUST$ENUM$DISR = Case1, [...]}, {RUST$ENUM$DISR = Case1, [...]}} // gdbr-check:$1 = generic_struct_style_enum::Regular::Case1{a: 0, b: 31868, c: 31868, d: 31868, e: 31868} // gdb-command:print case2 -// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, a = 0, b = 4369, c = 4369, d = 4369, e = 4369}, {RUST$ENUM$DISR = Case2, a = 0, b = 286331153, c = 286331153}, {RUST$ENUM$DISR = Case2, a = 0, b = 1229782938247303441}} +// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, [...]}, {RUST$ENUM$DISR = Case2, a = 0, b = 286331153, c = 286331153}, {RUST$ENUM$DISR = Case2, [...]}} // gdbr-check:$2 = generic_struct_style_enum::Regular::Case2{a: 0, b: 286331153, c: 286331153} // gdb-command:print case3 -// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, a = 0, b = 22873, c = 22873, d = 22873, e = 22873}, {RUST$ENUM$DISR = Case3, a = 0, b = 1499027801, c = 1499027801}, {RUST$ENUM$DISR = Case3, a = 0, b = 6438275382588823897}} +// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, a = 0, b = 6438275382588823897}} // gdbr-check:$3 = generic_struct_style_enum::Regular::Case3{a: 0, b: 6438275382588823897} // gdb-command:print univariant diff --git a/src/test/debuginfo/generic-tuple-style-enum.rs b/src/test/debuginfo/generic-tuple-style-enum.rs index 01d2ff4e334..9ada5fdeff7 100644 --- a/src/test/debuginfo/generic-tuple-style-enum.rs +++ b/src/test/debuginfo/generic-tuple-style-enum.rs @@ -19,15 +19,15 @@ // gdb-command:run // gdb-command:print case1 -// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, __0 = 0, __1 = 31868, __2 = 31868, __3 = 31868, __4 = 31868}, {RUST$ENUM$DISR = Case1, __0 = 0, __1 = 2088533116, __2 = 2088533116}, {RUST$ENUM$DISR = Case1, __0 = 0, __1 = 8970181431921507452}} +// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, __0 = 0, __1 = 31868, __2 = 31868, __3 = 31868, __4 = 31868}, {RUST$ENUM$DISR = Case1, [...]}, {RUST$ENUM$DISR = Case1, [...]}} // gdbr-check:$1 = generic_tuple_style_enum::Regular::Case1(0, 31868, 31868, 31868, 31868) // gdb-command:print case2 -// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, __0 = 0, __1 = 4369, __2 = 4369, __3 = 4369, __4 = 4369}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 286331153, __2 = 286331153}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 1229782938247303441}} +// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, [...]}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 286331153, __2 = 286331153}, {RUST$ENUM$DISR = Case2, [...]}} // gdbr-check:$2 = generic_tuple_style_enum::Regular::Case2(0, 286331153, 286331153) // gdb-command:print case3 -// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, __0 = 0, __1 = 22873, __2 = 22873, __3 = 22873, __4 = 22873}, {RUST$ENUM$DISR = Case3, __0 = 0, __1 = 1499027801, __2 = 1499027801}, {RUST$ENUM$DISR = Case3, __0 = 0, __1 = 6438275382588823897}} +// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, __0 = 0, __1 = 6438275382588823897}} // gdbr-check:$3 = generic_tuple_style_enum::Regular::Case3(0, 6438275382588823897) // gdb-command:print univariant diff --git a/src/test/debuginfo/struct-in-enum.rs b/src/test/debuginfo/struct-in-enum.rs index d0aceaa4f3f..ffd36ae14ad 100644 --- a/src/test/debuginfo/struct-in-enum.rs +++ b/src/test/debuginfo/struct-in-enum.rs @@ -19,11 +19,11 @@ // gdb-command:run // gdb-command:print case1 -// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, __0 = 0, __1 = {x = 2088533116, y = 2088533116, z = 31868}}, {RUST$ENUM$DISR = Case1, __0 = 0, __1 = 8970181431921507452, __2 = 31868}} +// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, __0 = 0, __1 = {x = 2088533116, y = 2088533116, z = 31868}}, {RUST$ENUM$DISR = Case1, [...]}} // gdbr-check:$1 = struct_in_enum::Regular::Case1(0, struct_in_enum::Struct {x: 2088533116, y: 2088533116, z: 31868}) // gdb-command:print case2 -// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, __0 = 0, __1 = {x = 286331153, y = 286331153, z = 4369}}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 1229782938247303441, __2 = 4369}} +// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, [...]}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 1229782938247303441, __2 = 4369}} // gdbr-check:$2 = struct_in_enum::Regular::Case2(0, 1229782938247303441, 4369) // gdb-command:print univariant diff --git a/src/test/debuginfo/struct-style-enum.rs b/src/test/debuginfo/struct-style-enum.rs index 8abc139eb11..b6196daaa46 100644 --- a/src/test/debuginfo/struct-style-enum.rs +++ b/src/test/debuginfo/struct-style-enum.rs @@ -19,15 +19,15 @@ // gdb-command:run // gdb-command:print case1 -// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, a = 0, b = 31868, c = 31868, d = 31868, e = 31868}, {RUST$ENUM$DISR = Case1, a = 0, b = 2088533116, c = 2088533116}, {RUST$ENUM$DISR = Case1, a = 0, b = 8970181431921507452}} +// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, a = 0, b = 31868, c = 31868, d = 31868, e = 31868}, {RUST$ENUM$DISR = Case1, [...]}, {RUST$ENUM$DISR = Case1, [...]}} // gdbr-check:$1 = struct_style_enum::Regular::Case1{a: 0, b: 31868, c: 31868, d: 31868, e: 31868} // gdb-command:print case2 -// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, a = 0, b = 4369, c = 4369, d = 4369, e = 4369}, {RUST$ENUM$DISR = Case2, a = 0, b = 286331153, c = 286331153}, {RUST$ENUM$DISR = Case2, a = 0, b = 1229782938247303441}} +// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, [...]}, {RUST$ENUM$DISR = Case2, a = 0, b = 286331153, c = 286331153}, {RUST$ENUM$DISR = Case2, [...]}} // gdbr-check:$2 = struct_style_enum::Regular::Case2{a: 0, b: 286331153, c: 286331153} // gdb-command:print case3 -// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, a = 0, b = 22873, c = 22873, d = 22873, e = 22873}, {RUST$ENUM$DISR = Case3, a = 0, b = 1499027801, c = 1499027801}, {RUST$ENUM$DISR = Case3, a = 0, b = 6438275382588823897}} +// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, a = 0, b = 6438275382588823897}} // gdbr-check:$3 = struct_style_enum::Regular::Case3{a: 0, b: 6438275382588823897} // gdb-command:print univariant diff --git a/src/test/debuginfo/tuple-style-enum.rs b/src/test/debuginfo/tuple-style-enum.rs index d05edec3e73..988f223b3bc 100644 --- a/src/test/debuginfo/tuple-style-enum.rs +++ b/src/test/debuginfo/tuple-style-enum.rs @@ -19,15 +19,15 @@ // gdb-command:run // gdb-command:print case1 -// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, __0 = 0, __1 = 31868, __2 = 31868, __3 = 31868, __4 = 31868}, {RUST$ENUM$DISR = Case1, __0 = 0, __1 = 2088533116, __2 = 2088533116}, {RUST$ENUM$DISR = Case1, __0 = 0, __1 = 8970181431921507452}} +// gdbg-check:$1 = {{RUST$ENUM$DISR = Case1, __0 = 0, __1 = 31868, __2 = 31868, __3 = 31868, __4 = 31868}, {RUST$ENUM$DISR = Case1, [...]}, {RUST$ENUM$DISR = Case1, [...]}} // gdbr-check:$1 = tuple_style_enum::Regular::Case1(0, 31868, 31868, 31868, 31868) // gdb-command:print case2 -// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, __0 = 0, __1 = 4369, __2 = 4369, __3 = 4369, __4 = 4369}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 286331153, __2 = 286331153}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 1229782938247303441}} +// gdbg-check:$2 = {{RUST$ENUM$DISR = Case2, [...]}, {RUST$ENUM$DISR = Case2, __0 = 0, __1 = 286331153, __2 = 286331153}, {RUST$ENUM$DISR = Case2, [...]}} // gdbr-check:$2 = tuple_style_enum::Regular::Case2(0, 286331153, 286331153) // gdb-command:print case3 -// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, __0 = 0, __1 = 22873, __2 = 22873, __3 = 22873, __4 = 22873}, {RUST$ENUM$DISR = Case3, __0 = 0, __1 = 1499027801, __2 = 1499027801}, {RUST$ENUM$DISR = Case3, __0 = 0, __1 = 6438275382588823897}} +// gdbg-check:$3 = {{RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, [...]}, {RUST$ENUM$DISR = Case3, __0 = 0, __1 = 6438275382588823897}} // gdbr-check:$3 = tuple_style_enum::Regular::Case3(0, 6438275382588823897) // gdb-command:print univariant diff --git a/src/test/debuginfo/union-smoke.rs b/src/test/debuginfo/union-smoke.rs index 26e73a08ea1..ada9e0b30cc 100644 --- a/src/test/debuginfo/union-smoke.rs +++ b/src/test/debuginfo/union-smoke.rs @@ -9,6 +9,7 @@ // except according to those terms. // min-lldb-version: 310 +// ignore-macos FIXME(#37479) // compile-flags:-g diff --git a/src/test/debuginfo/unique-enum.rs b/src/test/debuginfo/unique-enum.rs index e882544b802..cf8d90e30f1 100644 --- a/src/test/debuginfo/unique-enum.rs +++ b/src/test/debuginfo/unique-enum.rs @@ -18,11 +18,11 @@ // gdb-command:run // gdb-command:print *the_a -// gdbg-check:$1 = {{RUST$ENUM$DISR = TheA, x = 0, y = 8970181431921507452}, {RUST$ENUM$DISR = TheA, __0 = 0, __1 = 2088533116, __2 = 2088533116}} +// gdbg-check:$1 = {{RUST$ENUM$DISR = TheA, x = 0, y = 8970181431921507452}, {RUST$ENUM$DISR = TheA, [...]}} // gdbr-check:$1 = unique_enum::ABC::TheA{x: 0, y: 8970181431921507452} // gdb-command:print *the_b -// gdbg-check:$2 = {{RUST$ENUM$DISR = TheB, x = 0, y = 1229782938247303441}, {RUST$ENUM$DISR = TheB, __0 = 0, __1 = 286331153, __2 = 286331153}} +// gdbg-check:$2 = {{RUST$ENUM$DISR = TheB, [...]}, {RUST$ENUM$DISR = TheB, __0 = 0, __1 = 286331153, __2 = 286331153}} // gdbr-check:$2 = unique_enum::ABC::TheB(0, 286331153, 286331153) // gdb-command:print *univariant diff --git a/src/test/incremental/hashes/closure_expressions.rs b/src/test/incremental/hashes/closure_expressions.rs new file mode 100644 index 00000000000..38fe5cdffeb --- /dev/null +++ b/src/test/incremental/hashes/closure_expressions.rs @@ -0,0 +1,144 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// This test case tests the incremental compilation hash (ICH) implementation +// for closure expression. + +// The general pattern followed here is: Change one thing between rev1 and rev2 +// and make sure that the hash has changed, then change nothing between rev2 and +// rev3 and make sure that the hash has not changed. + +// must-compile-successfully +// revisions: cfail1 cfail2 cfail3 +// compile-flags: -Z query-dep-graph + +#![allow(warnings)] +#![feature(rustc_attrs)] +#![crate_type="rlib"] + + +// Change closure body --------------------------------------------------------- +#[cfg(cfail1)] +fn change_closure_body() { + let _ = || 1u32; +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_closure_body() { + let _ = || 3u32; +} + + + +// Add parameter --------------------------------------------------------------- +#[cfg(cfail1)] +fn add_parameter() { + let x = 0u32; + let _ = || x + 1; +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn add_parameter() { + let x = 0u32; + let _ = |x: u32| x + 1; +} + + + +// Change parameter pattern ---------------------------------------------------- +#[cfg(cfail1)] +fn change_parameter_pattern() { + let _ = |x: &u32| x; +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_parameter_pattern() { + let _ = |&x: &u32| x; +} + + + +// Add `move` to closure ------------------------------------------------------- +#[cfg(cfail1)] +fn add_move() { + let _ = || 1; +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn add_move() { + let _ = move || 1; +} + + + +// Add type ascription to parameter -------------------------------------------- +#[cfg(cfail1)] +fn add_type_ascription_to_parameter() { + let closure = |x| x + 1u32; + let _: u32 = closure(1); +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn add_type_ascription_to_parameter() { + let closure = |x: u32| x + 1u32; + let _: u32 = closure(1); +} + + + +// Change parameter type ------------------------------------------------------- +#[cfg(cfail1)] +fn change_parameter_type() { + let closure = |x: u32| (x as u64) + 1; + let _ = closure(1); +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_parameter_type() { + let closure = |x: u16| (x as u64) + 1; + let _ = closure(1); +} diff --git a/src/test/incremental/hashes/enum_constructors.rs b/src/test/incremental/hashes/enum_constructors.rs new file mode 100644 index 00000000000..7f991b30fc4 --- /dev/null +++ b/src/test/incremental/hashes/enum_constructors.rs @@ -0,0 +1,387 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// This test case tests the incremental compilation hash (ICH) implementation +// for struct constructor expressions. + +// The general pattern followed here is: Change one thing between rev1 and rev2 +// and make sure that the hash has changed, then change nothing between rev2 and +// rev3 and make sure that the hash has not changed. + +// must-compile-successfully +// revisions: cfail1 cfail2 cfail3 +// compile-flags: -Z query-dep-graph + +#![allow(warnings)] +#![feature(rustc_attrs)] +#![crate_type="rlib"] + + +enum Enum { + Struct { + x: i32, + y: i64, + z: i16, + }, + Tuple(i32, i64, i16) +} + +// Change field value (struct-like) ----------------------------------------- +#[cfg(cfail1)] +fn change_field_value_struct_like() -> Enum { + Enum::Struct { + x: 0, + y: 1, + z: 2, + } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_field_value_struct_like() -> Enum { + Enum::Struct { + x: 0, + y: 2, + z: 2, + } +} + + + +// Change field order (struct-like) ----------------------------------------- +#[cfg(cfail1)] +fn change_field_order_struct_like() -> Enum { + Enum::Struct { + x: 3, + y: 4, + z: 5, + } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_field_order_struct_like() -> Enum { + Enum::Struct { + y: 4, + x: 3, + z: 5, + } +} + + +enum Enum2 { + Struct { + x: i8, + y: i8, + z: i8, + }, + Struct2 { + x: i8, + y: i8, + z: i8, + }, + Tuple(u16, u16, u16), + Tuple2(u64, u64, u64), +} + +// Change constructor path (struct-like) ------------------------------------ +#[cfg(cfail1)] +fn change_constructor_path_struct_like() { + let _ = Enum::Struct { + x: 0, + y: 1, + z: 2, + }; +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_constructor_path_struct_like() { + let _ = Enum2::Struct { + x: 0, + y: 1, + z: 2, + }; +} + + + +// Change variant (regular struct) ------------------------------------ +#[cfg(cfail1)] +fn change_constructor_variant_struct_like() { + let _ = Enum2::Struct { + x: 0, + y: 1, + z: 2, + }; +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_constructor_variant_struct_like() { + let _ = Enum2::Struct2 { + x: 0, + y: 1, + z: 2, + }; +} + + +// Change constructor path indirectly (struct-like) ------------------------- +mod change_constructor_path_indirectly_struct_like { + #[cfg(cfail1)] + use super::Enum as TheEnum; + #[cfg(not(cfail1))] + use super::Enum2 as TheEnum; + + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + fn function() -> TheEnum { + TheEnum::Struct { + x: 0, + y: 1, + z: 2, + } + } +} + + +// Change constructor variant indirectly (struct-like) --------------------------- +mod change_constructor_variant_indirectly_struct_like { + use super::Enum2; + #[cfg(cfail1)] + use super::Enum2::Struct as Variant; + #[cfg(not(cfail1))] + use super::Enum2::Struct2 as Variant; + + #[rustc_clean(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_metadata_clean(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + fn function() -> Enum2 { + Variant { + x: 0, + y: 1, + z: 2, + } + } +} + + +// Change field value (tuple-like) ------------------------------------------- +#[cfg(cfail1)] +fn change_field_value_tuple_like() -> Enum { + Enum::Tuple(0, 1, 2) +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_field_value_tuple_like() -> Enum { + Enum::Tuple(0, 1, 3) +} + + + +// Change constructor path (tuple-like) -------------------------------------- +#[cfg(cfail1)] +fn change_constructor_path_tuple_like() { + let _ = Enum::Tuple(0, 1, 2); +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_constructor_path_tuple_like() { + let _ = Enum2::Tuple(0, 1, 2); +} + + + +// Change constructor variant (tuple-like) -------------------------------------- +#[cfg(cfail1)] +fn change_constructor_variant_tuple_like() { + let _ = Enum2::Tuple(0, 1, 2); +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_constructor_variant_tuple_like() { + let _ = Enum2::Tuple2(0, 1, 2); +} + + +// Change constructor path indirectly (tuple-like) --------------------------- +mod change_constructor_path_indirectly_tuple_like { + #[cfg(cfail1)] + use super::Enum as TheEnum; + #[cfg(not(cfail1))] + use super::Enum2 as TheEnum; + + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + fn function() -> TheEnum { + TheEnum::Tuple(0, 1, 2) + } +} + + + +// Change constructor variant indirectly (tuple-like) --------------------------- +mod change_constructor_variant_indirectly_tuple_like { + use super::Enum2; + #[cfg(cfail1)] + use super::Enum2::Tuple as Variant; + #[cfg(not(cfail1))] + use super::Enum2::Tuple2 as Variant; + + #[rustc_clean(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_metadata_clean(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + fn function() -> Enum2 { + Variant(0, 1, 2) + } +} + + +enum Clike { + A, + B, + C +} + +enum Clike2 { + B, + C, + D +} + +// Change constructor path (C-like) -------------------------------------- +#[cfg(cfail1)] +fn change_constructor_path_c_like() { + let _ = Clike::B; +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_constructor_path_c_like() { + let _ = Clike2::B; +} + + + +// Change constructor variant (C-like) -------------------------------------- +#[cfg(cfail1)] +fn change_constructor_variant_c_like() { + let _ = Clike::A; +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_constructor_variant_c_like() { + let _ = Clike::C; +} + + +// Change constructor path indirectly (C-like) --------------------------- +mod change_constructor_path_indirectly_c_like { + #[cfg(cfail1)] + use super::Clike as TheEnum; + #[cfg(not(cfail1))] + use super::Clike2 as TheEnum; + + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + fn function() -> TheEnum { + TheEnum::B + } +} + + + +// Change constructor variant indirectly (C-like) --------------------------- +mod change_constructor_variant_indirectly_c_like { + use super::Clike; + #[cfg(cfail1)] + use super::Clike::A as Variant; + #[cfg(not(cfail1))] + use super::Clike::B as Variant; + + #[rustc_clean(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_metadata_clean(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + fn function() -> Clike { + Variant + } +} diff --git a/src/test/incremental/hashes/exported_vs_not.rs b/src/test/incremental/hashes/exported_vs_not.rs new file mode 100644 index 00000000000..082badacc6c --- /dev/null +++ b/src/test/incremental/hashes/exported_vs_not.rs @@ -0,0 +1,86 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully +// revisions: cfail1 cfail2 cfail3 +// compile-flags: -Z query-dep-graph + +#![allow(warnings)] +#![feature(rustc_attrs)] +#![crate_type="rlib"] + +// Case 1: The function body is not exported to metadata. If the body changes, +// the hash of the HirBody node should change, but not the hash of +// either the Hir or the Metadata node. + +#[cfg(cfail1)] +pub fn body_not_exported_to_metadata() -> u32 { + 1 +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +pub fn body_not_exported_to_metadata() -> u32 { + 2 +} + + + +// Case 2: The function body *is* exported to metadata because the function is +// marked as #[inline]. Only the hash of the Hir depnode should be +// unaffected by a change to the body. + +#[cfg(cfail1)] +#[inline] +pub fn body_exported_to_metadata_because_of_inline() -> u32 { + 1 +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_dirty(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +#[inline] +pub fn body_exported_to_metadata_because_of_inline() -> u32 { + 2 +} + + + +// Case 2: The function body *is* exported to metadata because the function is +// generic. Only the hash of the Hir depnode should be +// unaffected by a change to the body. + +#[cfg(cfail1)] +#[inline] +pub fn body_exported_to_metadata_because_of_generic() -> u32 { + 1 +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_dirty(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +#[inline] +pub fn body_exported_to_metadata_because_of_generic() -> u32 { + 2 +} + diff --git a/src/test/incremental/hashes/indexing_expressions.rs b/src/test/incremental/hashes/indexing_expressions.rs new file mode 100644 index 00000000000..bb31982d93f --- /dev/null +++ b/src/test/incremental/hashes/indexing_expressions.rs @@ -0,0 +1,157 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// This test case tests the incremental compilation hash (ICH) implementation +// for closure expression. + +// The general pattern followed here is: Change one thing between rev1 and rev2 +// and make sure that the hash has changed, then change nothing between rev2 and +// rev3 and make sure that the hash has not changed. + +// must-compile-successfully +// revisions: cfail1 cfail2 cfail3 +// compile-flags: -Z query-dep-graph + +#![allow(warnings)] +#![feature(rustc_attrs)] +#![crate_type="rlib"] +#![feature(inclusive_range_syntax)] + +// Change simple index --------------------------------------------------------- +#[cfg(cfail1)] +fn change_simple_index(slice: &[u32]) -> u32 { + slice[3] +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_simple_index(slice: &[u32]) -> u32 { + slice[4] +} + + + +// Change lower bound ---------------------------------------------------------- +#[cfg(cfail1)] +fn change_lower_bound(slice: &[u32]) -> &[u32] { + &slice[3..5] +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_lower_bound(slice: &[u32]) -> &[u32] { + &slice[2..5] +} + + + +// Change upper bound ---------------------------------------------------------- +#[cfg(cfail1)] +fn change_upper_bound(slice: &[u32]) -> &[u32] { + &slice[3..5] +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_upper_bound(slice: &[u32]) -> &[u32] { + &slice[3..7] +} + + + +// Add lower bound ------------------------------------------------------------- +#[cfg(cfail1)] +fn add_lower_bound(slice: &[u32]) -> &[u32] { + &slice[..4] +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn add_lower_bound(slice: &[u32]) -> &[u32] { + &slice[3..4] +} + + + +// Add upper bound ------------------------------------------------------------- +#[cfg(cfail1)] +fn add_upper_bound(slice: &[u32]) -> &[u32] { + &slice[3..] +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn add_upper_bound(slice: &[u32]) -> &[u32] { + &slice[3..7] +} + + + +// Change mutability ----------------------------------------------------------- +#[cfg(cfail1)] +fn change_mutability(slice: &mut [u32]) -> u32 { + (&mut slice[3..5])[0] +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn change_mutability(slice: &mut [u32]) -> u32 { + (&slice[3..5])[0] +} + + + +// Exclusive to inclusive range ------------------------------------------------ +#[cfg(cfail1)] +fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { + &slice[3..7] +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { + &slice[3...7] +} diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs index f7a390e8745..5c37bc13359 100644 --- a/src/test/incremental/hashes/inherent_impls.rs +++ b/src/test/incremental/hashes/inherent_impls.rs @@ -107,7 +107,7 @@ impl Foo { pub fn method_selfness(&self) { } } -// Change Method Selfmutness ----------------------------------------------------------- +// Change Method Selfmutness --------------------------------------------------- #[cfg(cfail1)] impl Foo { pub fn method_selfmutness(&self) { } @@ -126,3 +126,411 @@ impl Foo { pub fn method_selfmutness(&mut self) { } } + + +// Add Method To Impl ---------------------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn add_method_to_impl1(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_dirty(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_clean(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_clean(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_method_to_impl1(&self) { } + + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_method_to_impl2(&self) { } +} + + + +// Add Method Parameter -------------------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn add_method_parameter(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_method_parameter(&self, _: i32) { } +} + + + +// Change Method Parameter Name ------------------------------------------------ +#[cfg(cfail1)] +impl Foo { + pub fn change_method_parameter_name(&self, a: i64) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn change_method_parameter_name(&self, b: i64) { } +} + + + +// Change Method Return Type --------------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn change_method_return_type(&self) -> u16 { 0 } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn change_method_return_type(&self) -> u8 { 0 } +} + + + +// Make Method #[inline] ------------------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn make_method_inline(&self) -> u8 { 0 } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + #[inline] + pub fn make_method_inline(&self) -> u8 { 0 } +} + + + +// Change order of parameters ------------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn change_method_parameter_order(&self, a: i64, b: i64) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn change_method_parameter_order(&self, b: i64, a: i64) { } +} + + + +// Make method unsafe ---------------------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn make_method_unsafe(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub unsafe fn make_method_unsafe(&self) { } +} + + + +// Make method extern ---------------------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn make_method_extern(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub extern fn make_method_extern(&self) { } +} + + + +// Change method calling convention -------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub extern "C" fn change_method_calling_convention(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub extern "system" fn change_method_calling_convention(&self) { } +} + + + +// Add Lifetime Parameter to Method -------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn add_lifetime_parameter_to_method(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_lifetime_parameter_to_method<'a>(&self) { } +} + + + +// Add Type Parameter To Method ------------------------------------------------ +#[cfg(cfail1)] +impl Foo { + pub fn add_type_parameter_to_method(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_type_parameter_to_method<T>(&self) { } +} + + + +// Add Lifetime Bound to Lifetime Parameter of Method -------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn add_lifetime_bound_to_lifetime_param_of_method<'a, 'b>(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_lifetime_bound_to_lifetime_param_of_method<'a, 'b: 'a>(&self) { } +} + + + +// Add Lifetime Bound to Type Parameter of Method ------------------------------ +#[cfg(cfail1)] +impl Foo { + pub fn add_lifetime_bound_to_type_param_of_method<'a, T>(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_lifetime_bound_to_type_param_of_method<'a, T: 'a>(&self) { } +} + + + +// Add Trait Bound to Type Parameter of Method ------------------------------ +#[cfg(cfail1)] +impl Foo { + pub fn add_trait_bound_to_type_param_of_method<T>(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_trait_bound_to_type_param_of_method<T: Clone>(&self) { } +} + + + +// Add #[no_mangle] to Method -------------------------------------------------- +#[cfg(cfail1)] +impl Foo { + pub fn add_no_mangle_to_method(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Foo { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + #[no_mangle] + pub fn add_no_mangle_to_method(&self) { } +} + + + +struct Bar<T>(T); + +// Add Type Parameter To Impl -------------------------------------------------- +#[cfg(cfail1)] +impl Bar<u32> { + pub fn add_type_parameter_to_impl(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_dirty(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl<T> Bar<T> { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_type_parameter_to_impl(&self) { } +} + + + +// Change Self Type of Impl ---------------------------------------------------- +#[cfg(cfail1)] +impl Bar<u32> { + pub fn change_impl_self_type(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_dirty(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl Bar<u64> { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn change_impl_self_type(&self) { } +} + + + +// Add Lifetime Bound to Impl -------------------------------------------------- +#[cfg(cfail1)] +impl<T> Bar<T> { + pub fn add_lifetime_bound_to_impl_parameter(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_dirty(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl<T: 'static> Bar<T> { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_lifetime_bound_to_impl_parameter(&self) { } +} + + + +// Add Trait Bound to Impl Parameter ------------------------------------------- +#[cfg(cfail1)] +impl<T> Bar<T> { + pub fn add_trait_bound_to_impl_parameter(&self) { } +} + +#[cfg(not(cfail1))] +#[rustc_dirty(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_metadata_dirty(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +impl<T: Clone> Bar<T> { + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] + pub fn add_trait_bound_to_impl_parameter(&self) { } +} diff --git a/src/test/incremental/hashes/inline_asm.rs b/src/test/incremental/hashes/inline_asm.rs new file mode 100644 index 00000000000..a1057c036d6 --- /dev/null +++ b/src/test/incremental/hashes/inline_asm.rs @@ -0,0 +1,265 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// This test case tests the incremental compilation hash (ICH) implementation +// for inline asm. + +// The general pattern followed here is: Change one thing between rev1 and rev2 +// and make sure that the hash has changed, then change nothing between rev2 and +// rev3 and make sure that the hash has not changed. + +// must-compile-successfully +// revisions: cfail1 cfail2 cfail3 +// compile-flags: -Z query-dep-graph + +#![allow(warnings)] +#![feature(rustc_attrs)] +#![feature(asm)] +#![crate_type="rlib"] + + + +// Change template ------------------------------------------------------------- +#[cfg(cfail1)] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_template(a: i32) -> i32 { + let c: i32; + unsafe { + asm!("add 1, $0" + : "=r"(c) + : "0"(a) + : + : + ); + } + c +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_template(a: i32) -> i32 { + let c: i32; + unsafe { + asm!("add 2, $0" + : "=r"(c) + : "0"(a) + : + : + ); + } + c +} + + + +// Change output ------------------------------------------------------------- +#[cfg(cfail1)] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_output(a: i32) -> i32 { + let mut _out1: i32 = 0; + let mut _out2: i32 = 0; + unsafe { + asm!("add 1, $0" + : "=r"(_out1) + : "0"(a) + : + : + ); + } + _out1 +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_output(a: i32) -> i32 { + let mut _out1: i32 = 0; + let mut _out2: i32 = 0; + unsafe { + asm!("add 1, $0" + : "=r"(_out2) + : "0"(a) + : + : + ); + } + _out1 +} + + + +// Change input ------------------------------------------------------------- +#[cfg(cfail1)] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_input(_a: i32, _b: i32) -> i32 { + let _out; + unsafe { + asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : + ); + } + _out +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_input(_a: i32, _b: i32) -> i32 { + let _out; + unsafe { + asm!("add 1, $0" + : "=r"(_out) + : "0"(_b) + : + : + ); + } + _out +} + + + +// Change input constraint ----------------------------------------------------- +#[cfg(cfail1)] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_input_constraint(_a: i32, _b: i32) -> i32 { + let _out; + unsafe { + asm!("add 1, $0" + : "=r"(_out) + : "0"(_a), "r"(_b) + : + : + ); + } + _out +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_input_constraint(_a: i32, _b: i32) -> i32 { + let _out; + unsafe { + asm!("add 1, $0" + : "=r"(_out) + : "r"(_a), "0"(_b) + : + : + ); + } + _out +} + + + +// Change clobber -------------------------------------------------------------- +#[cfg(cfail1)] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_clobber(_a: i32) -> i32 { + let _out; + unsafe { + asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : + ); + } + _out +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_clobber(_a: i32) -> i32 { + let _out; + unsafe { + asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : "eax" + : + ); + } + _out +} + + + +// Change options -------------------------------------------------------------- +#[cfg(cfail1)] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_options(_a: i32) -> i32 { + let _out; + unsafe { + asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : + ); + } + _out +} + +#[cfg(not(cfail1))] +#[rustc_clean(label="Hir", cfg="cfail2")] +#[rustc_clean(label="Hir", cfg="cfail3")] +#[rustc_dirty(label="HirBody", cfg="cfail2")] +#[rustc_clean(label="HirBody", cfg="cfail3")] +#[rustc_metadata_clean(cfg="cfail2")] +#[rustc_metadata_clean(cfg="cfail3")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn change_options(_a: i32) -> i32 { + let _out; + unsafe { + asm!("add 1, $0" + : "=r"(_out) + : "0"(_a) + : + : "volatile" + ); + } + _out +} + + + diff --git a/src/test/incremental/hashes/struct_constructors.rs b/src/test/incremental/hashes/struct_constructors.rs index 6a9f4698bf8..0e23d953baf 100644 --- a/src/test/incremental/hashes/struct_constructors.rs +++ b/src/test/incremental/hashes/struct_constructors.rs @@ -202,6 +202,12 @@ mod change_constructor_path_indirectly_regular_struct { #[cfg(not(cfail1))] use super::RegularStruct2 as Struct; + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] fn function() -> Struct { Struct { x: 0, @@ -262,6 +268,12 @@ mod change_constructor_path_indirectly_tuple_struct { #[cfg(not(cfail1))] use super::TupleStruct2 as Struct; + #[rustc_dirty(label="Hir", cfg="cfail2")] + #[rustc_clean(label="Hir", cfg="cfail3")] + #[rustc_dirty(label="HirBody", cfg="cfail2")] + #[rustc_clean(label="HirBody", cfg="cfail3")] + #[rustc_metadata_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail3")] fn function() -> Struct { Struct(0, 1, 2) } diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs new file mode 100644 index 00000000000..26b042d0343 --- /dev/null +++ b/src/test/mir-opt/copy_propagation.rs @@ -0,0 +1,34 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn test(x: u32) -> u32 { + let y = x; + y +} + +fn main() { } + +// END RUST SOURCE +// START rustc.node4.CopyPropagation.before.mir +// bb0: { +// _2 = _1; +// _4 = _2; +// _3 = _4; +// _5 = _3; +// _0 = _5; +// return; +// } +// END rustc.node4.CopyPropagation.before.mir +// START rustc.node4.CopyPropagation.after.mir +// bb0: { +// _0 = _1; +// return; +// } +// END rustc.node4.CopyPropagation.after.mir diff --git a/src/test/parse-fail/where_with_bound.rs b/src/test/parse-fail/where_with_bound.rs new file mode 100644 index 00000000000..cb57500df79 --- /dev/null +++ b/src/test/parse-fail/where_with_bound.rs @@ -0,0 +1,16 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +fn foo<T>() where <T>::Item: ToString, T: Iterator { } + //~^ syntax `where<T>` is reserved for future use + +fn main() {} diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index fdb7f9c68b9..6c74e7758c4 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -39,7 +39,7 @@ pub fn bar() ({ - (($crate::fmt::format as + ((::fmt::format as fn(std::fmt::Arguments<'_>) -> std::string::String {std::fmt::format})(((<::std::fmt::Arguments>::new_v1 as fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})(({ diff --git a/src/test/run-make/issue-38237/Makefile b/src/test/run-make/issue-38237/Makefile new file mode 100644 index 00000000000..0a681401b1a --- /dev/null +++ b/src/test/run-make/issue-38237/Makefile @@ -0,0 +1,5 @@ +-include ../tools.mk + +all: + $(RUSTC) foo.rs; $(RUSTC) bar.rs + $(RUSTDOC) baz.rs -L $(TMPDIR) -o $(TMPDIR) diff --git a/src/test/run-make/issue-38237/bar.rs b/src/test/run-make/issue-38237/bar.rs new file mode 100644 index 00000000000..794e08c2fe3 --- /dev/null +++ b/src/test/run-make/issue-38237/bar.rs @@ -0,0 +1,14 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +#[derive(Debug)] +pub struct S; diff --git a/src/test/run-make/issue-38237/baz.rs b/src/test/run-make/issue-38237/baz.rs new file mode 100644 index 00000000000..c2a2c89db01 --- /dev/null +++ b/src/test/run-make/issue-38237/baz.rs @@ -0,0 +1,18 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate foo; +extern crate bar; + +pub struct Bar; +impl ::std::ops::Deref for Bar { + type Target = bar::S; + fn deref(&self) -> &Self::Target { unimplemented!() } +} diff --git a/src/test/run-make/issue-38237/foo.rs b/src/test/run-make/issue-38237/foo.rs new file mode 100644 index 00000000000..c291ffbf4e8 --- /dev/null +++ b/src/test/run-make/issue-38237/foo.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "proc-macro"] +#![feature(proc_macro, proc_macro_lib)] + +extern crate proc_macro; + +#[proc_macro_derive(A)] +pub fn derive(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { ts } + +#[derive(Debug)] +struct S; diff --git a/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs b/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs index 07f7d6bad7b..e46e4fb3766 100644 --- a/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs +++ b/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs @@ -31,7 +31,7 @@ use rustc_plugin::Registry; #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { - reg.register_syntax_extension( + reg.register_custom_derive( Symbol::intern("derive_TotalSum"), MultiDecorator(box expand)); } diff --git a/src/test/run-pass/auxiliary/issue_38190.rs b/src/test/run-pass/auxiliary/issue_38190.rs new file mode 100644 index 00000000000..7fc4390d6dc --- /dev/null +++ b/src/test/run-pass/auxiliary/issue_38190.rs @@ -0,0 +1,12 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_export] +macro_rules! m { ([$i:item]) => {} } diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs index c438c17f51e..75c665b04a1 100644 --- a/src/test/run-pass/backtrace.rs +++ b/src/test/run-pass/backtrace.rs @@ -10,6 +10,7 @@ // ignore-android FIXME #17520 // ignore-emscripten spawning processes is not supported +// ignore-openbsd no support for libbacktrace without filename // compile-flags:-g use std::env; diff --git a/src/test/run-pass/closure-immediate.rs b/src/test/run-pass/closure-immediate.rs new file mode 100644 index 00000000000..e566c105835 --- /dev/null +++ b/src/test/run-pass/closure-immediate.rs @@ -0,0 +1,22 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// After the work to reoptimize structs, it became possible for immediate logic to fail. +// This test verifies that it actually works. + +fn main() { + let c = |a: u8, b: u16, c: u8| { + assert_eq!(a, 1); + assert_eq!(b, 2); + assert_eq!(c, 3); + }; + c(1, 2, 3); +} diff --git a/src/test/run-pass/enum-size-variance.rs b/src/test/run-pass/enum-size-variance.rs index 26deb0ed72a..a3e95a15341 100644 --- a/src/test/run-pass/enum-size-variance.rs +++ b/src/test/run-pass/enum-size-variance.rs @@ -11,6 +11,9 @@ #![warn(variant_size_differences)] #![allow(dead_code)] +// Note that the following test works because all fields of the enum variants are of the same size. +// If this test is modified and the reordering logic in librustc/ty/layout.rs kicks in, it fails. + enum Enum1 { } enum Enum2 { A, B, C } diff --git a/src/test/run-pass/extern-pass-empty.rs b/src/test/run-pass/extern-pass-empty.rs index cce7dc5bf32..2606c928680 100644 --- a/src/test/run-pass/extern-pass-empty.rs +++ b/src/test/run-pass/extern-pass-empty.rs @@ -14,11 +14,13 @@ // ignore-msvc // ignore-emscripten +#[repr(C)] struct TwoU8s { one: u8, two: u8, } +#[repr(C)] struct ManyInts { arg1: i8, arg2: i16, @@ -28,6 +30,7 @@ struct ManyInts { arg6: TwoU8s, } +#[repr(C)] struct Empty; #[link(name = "rust_test_helpers", kind = "static")] diff --git a/src/test/compile-fail/unresolved_static_type_field.rs b/src/test/run-pass/issue-38190.rs index 80f6108f02d..ed9bf9e8095 100644 --- a/src/test/compile-fail/unresolved_static_type_field.rs +++ b/src/test/run-pass/issue-38190.rs @@ -8,17 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn f(_: bool) {} +// aux-build:issue_38190.rs +// ignore-pretty issue #37195 -struct Foo { - cx: bool, -} +#[macro_use] +extern crate issue_38190; -impl Foo { - fn bar() { - f(cx); //~ ERROR E0425 - //~| HELP this is an associated function - } +mod auxiliary { + m!([mod issue_38190;]); } fn main() {} diff --git a/src/test/run-pass/issue-38437.rs b/src/test/run-pass/issue-38437.rs new file mode 100644 index 00000000000..a6e7df1c010 --- /dev/null +++ b/src/test/run-pass/issue-38437.rs @@ -0,0 +1,54 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that drop elaboration clears the "master" discriminant +// drop flag even if it protects no fields. + +struct Good(usize); +impl Drop for Good { + #[inline(never)] + fn drop(&mut self) { + println!("dropping Good({})", self.0); + } +} + +struct Void; +impl Drop for Void { + #[inline(never)] + fn drop(&mut self) { + panic!("Suddenly, a Void appears."); + } +} + +enum E { + Never(Void), + Fine(Good) +} + +fn main() { + let mut go = true; + + loop { + let next; + match go { + true => next = E::Fine(Good(123)), + false => return, + } + + match next { + E::Never(_) => return, + E::Fine(_good) => go = false, + } + + // `next` is dropped and StorageDead'd here. We must reset the + // discriminant's drop flag to avoid random variants being + // dropped. + } +} diff --git a/src/test/compile-fail/E0248.rs b/src/test/run-pass/issue-38556.rs index 25568a323e1..f6f334f650b 100644 --- a/src/test/compile-fail/E0248.rs +++ b/src/test/run-pass/issue-38556.rs @@ -8,11 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum Foo { - Bar(u32), +pub struct Foo; + +macro_rules! reexport { + () => { use Foo as Bar; } } -fn do_something(x: Foo::Bar) { } //~ ERROR E0248 - //~| NOTE value used as a type +reexport!(); + fn main() { + use Bar; + fn f(_: Bar) {} } diff --git a/src/test/run-pass/issue-8521.rs b/src/test/run-pass/issue-8521.rs new file mode 100644 index 00000000000..ce362c4bcd1 --- /dev/null +++ b/src/test/run-pass/issue-8521.rs @@ -0,0 +1,34 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo1 {} + +trait A {} + +macro_rules! foo1(($t:path) => { + impl<T: $t> Foo1 for T {} +}); + +foo1!(A); + +trait Foo2 {} + +trait B<T> {} + +#[allow(unused)] +struct C {} + +macro_rules! foo2(($t:path) => { + impl<T: $t> Foo2 for T {} +}); + +foo2!(B<C>); + +fn main() {} diff --git a/src/test/run-pass/multiple-reprs.rs b/src/test/run-pass/multiple-reprs.rs new file mode 100644 index 00000000000..c2fe943eed8 --- /dev/null +++ b/src/test/run-pass/multiple-reprs.rs @@ -0,0 +1,43 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use std::mem::size_of; + +// The two enums that follow are designed so that bugs trigger layout optimization. +// Specifically, if either of the following reprs used here is not detected by the compiler, +// then the sizes will be wrong. + +#[repr(C, u8)] +enum E1 { + A(u8, u16, u8), + B(u8, u16, u8) +} + +#[repr(u8, C)] +enum E2 { + A(u8, u16, u8), + B(u8, u16, u8) +} + +// From pr 37429 + +#[repr(C,packed)] +pub struct p0f_api_query { + pub magic: u32, + pub addr_type: u8, + pub addr: [u8; 16], +} + +pub fn main() { + assert_eq!(size_of::<E1>(), 6); + assert_eq!(size_of::<E2>(), 6); + assert_eq!(size_of::<p0f_api_query>(), 21); +} diff --git a/src/test/run-pass/nonzero-enum.rs b/src/test/run-pass/nonzero-enum.rs index 266506e04b7..fc92c9df9f7 100644 --- a/src/test/run-pass/nonzero-enum.rs +++ b/src/test/run-pass/nonzero-enum.rs @@ -26,8 +26,7 @@ fn main() { assert_eq!(size_of::<E>(), 1); assert_eq!(size_of::<Option<E>>(), 1); assert_eq!(size_of::<Result<E, ()>>(), 1); - assert_eq!(size_of::<S>(), 4); - assert_eq!(size_of::<Option<S>>(), 4); + assert_eq!(size_of::<Option<S>>(), size_of::<S>()); let enone = None::<E>; let esome = Some(E::A); if let Some(..) = enone { diff --git a/src/test/run-pass/struct-return.rs b/src/test/run-pass/struct-return.rs index 3d4601ad0cf..ed618cea98a 100644 --- a/src/test/run-pass/struct-return.rs +++ b/src/test/run-pass/struct-return.rs @@ -9,9 +9,11 @@ // except according to those terms. // +#[repr(C)] #[derive(Copy, Clone)] pub struct Quad { a: u64, b: u64, c: u64, d: u64 } +#[repr(C)] #[derive(Copy, Clone)] pub struct Floats { a: f64, b: u8, c: f64 } diff --git a/src/test/run-pass/trans-object-shim.rs b/src/test/run-pass/trans-object-shim.rs new file mode 100644 index 00000000000..5fbfef05e10 --- /dev/null +++ b/src/test/run-pass/trans-object-shim.rs @@ -0,0 +1,14 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + assert_eq!((ToString::to_string as fn(&(ToString+'static)) -> String)(&"foo"), + String::from("foo")); +} diff --git a/src/test/run-pass/type-sizes.rs b/src/test/run-pass/type-sizes.rs index 86159ce340b..bbb01eaaf46 100644 --- a/src/test/run-pass/type-sizes.rs +++ b/src/test/run-pass/type-sizes.rs @@ -26,6 +26,7 @@ enum e2 { a(u32), b } +#[repr(C, u8)] enum e3 { a([u16; 0], u8), b } diff --git a/src/test/rustdoc/search-index-summaries.rs b/src/test/rustdoc/search-index-summaries.rs new file mode 100644 index 00000000000..dd195dc33e4 --- /dev/null +++ b/src/test/rustdoc/search-index-summaries.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +// @has 'search-index.js' 'Foo short link.' +// @!has - 'www.example.com' +// @!has - 'More Foo.' + +/// Foo short [link](https://www.example.com/). +/// +/// More Foo. +pub struct Foo; diff --git a/src/test/ui/codemap_tests/tab.stderr b/src/test/ui/codemap_tests/tab.stderr index f865f0a5f23..73d548009dd 100644 --- a/src/test/ui/codemap_tests/tab.stderr +++ b/src/test/ui/codemap_tests/tab.stderr @@ -1,8 +1,8 @@ -error[E0425]: unresolved name `bar` +error[E0425]: unresolved value `bar` --> $DIR/tab.rs:14:2 | 14 | \tbar; - | \t^^^ unresolved name + | \t^^^ no resolution found error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/two_files.stderr b/src/test/ui/codemap_tests/two_files.stderr index d05e6eb2bbe..9db43dde1ac 100644 --- a/src/test/ui/codemap_tests/two_files.stderr +++ b/src/test/ui/codemap_tests/two_files.stderr @@ -1,10 +1,8 @@ -error[E0404]: `Bar` is not a trait +error[E0404]: expected trait, found type alias `Bar` --> $DIR/two_files.rs:15:6 | 15 | impl Bar for Baz { } - | ^^^ expected trait, found type alias - | - = note: type aliases cannot be used for traits + | ^^^ type aliases cannot be used for traits error: cannot continue compilation due to previous error diff --git a/src/test/ui/macros/macro-backtrace-nested.stderr b/src/test/ui/macros/macro-backtrace-nested.stderr index 1c7fac894f9..27514093621 100644 --- a/src/test/ui/macros/macro-backtrace-nested.stderr +++ b/src/test/ui/macros/macro-backtrace-nested.stderr @@ -1,17 +1,17 @@ -error[E0425]: unresolved name `fake` +error[E0425]: unresolved value `fake` --> $DIR/macro-backtrace-nested.rs:15:12 | 15 | () => (fake) - | ^^^^ unresolved name + | ^^^^ no resolution found ... 27 | 1 + call_nested_expr!(); | ------------------- in this macro invocation -error[E0425]: unresolved name `fake` +error[E0425]: unresolved value `fake` --> $DIR/macro-backtrace-nested.rs:15:12 | 15 | () => (fake) - | ^^^^ unresolved name + | ^^^^ no resolution found ... 28 | call_nested_expr_sum!(); | ------------------------ in this macro invocation diff --git a/src/test/ui/macros/macro_path_as_generic_bound.rs b/src/test/ui/macros/macro_path_as_generic_bound.rs new file mode 100644 index 00000000000..781ea30ed8b --- /dev/null +++ b/src/test/ui/macros/macro_path_as_generic_bound.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo {} + +macro_rules! foo(($t:path) => { + impl<T: $t> Foo for T {} +}); + +foo!(m::m2::A); + +fn main() {} diff --git a/src/test/ui/macros/macro_path_as_generic_bound.stderr b/src/test/ui/macros/macro_path_as_generic_bound.stderr new file mode 100644 index 00000000000..96635032105 --- /dev/null +++ b/src/test/ui/macros/macro_path_as_generic_bound.stderr @@ -0,0 +1,11 @@ +error[E0433]: failed to resolve. Use of undeclared type or module `m` + --> $DIR/macro_path_as_generic_bound.rs:17:6 + | +17 | foo!(m::m2::A); + | -----^^^^^^^^-- + | | | + | | Use of undeclared type or module `m` + | in this macro invocation + +error: cannot continue compilation due to previous error + diff --git a/src/test/ui/mismatched_types/cast-rfc0401.rs b/src/test/ui/mismatched_types/cast-rfc0401.rs new file mode 100644 index 00000000000..f72be0d7054 --- /dev/null +++ b/src/test/ui/mismatched_types/cast-rfc0401.rs @@ -0,0 +1,82 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn illegal_cast<U:?Sized,V:?Sized>(u: *const U) -> *const V +{ + u as *const V +} + +fn illegal_cast_2<U:?Sized>(u: *const U) -> *const str +{ + u as *const str +} + +trait Foo { fn foo(&self) {} } +impl<T> Foo for T {} + +trait Bar { fn foo(&self) {} } +impl<T> Bar for T {} + +enum E { + A, B +} + +fn main() +{ + let f: f32 = 1.2; + let v = 0 as *const u8; + let fat_v : *const [u8] = unsafe { &*(0 as *const [u8; 1])}; + let fat_sv : *const [i8] = unsafe { &*(0 as *const [i8; 1])}; + let foo: &Foo = &f; + + let _ = v as &u8; + let _ = v as E; + let _ = v as fn(); + let _ = v as (u32,); + let _ = Some(&v) as *const u8; + + let _ = v as f32; + let _ = main as f64; + let _ = &v as usize; + let _ = f as *const u8; + let _ = 3_i32 as bool; + let _ = E::A as bool; + let _ = 0x61u32 as char; + + let _ = false as f32; + let _ = E::A as f32; + let _ = 'a' as f32; + + let _ = false as *const u8; + let _ = E::A as *const u8; + let _ = 'a' as *const u8; + + let _ = 42usize as *const [u8]; + let _ = v as *const [u8]; + let _ = fat_v as *const Foo; + let _ = foo as *const str; + let _ = foo as *mut str; + let _ = main as *mut str; + let _ = &f as *mut f32; + let _ = &f as *const f64; + let _ = fat_sv as usize; + + let a : *const str = "hello"; + let _ = a as *const Foo; + + // check no error cascade + let _ = main.f as *const u32; + + let cf: *const Foo = &0; + let _ = cf as *const [u16]; + let _ = cf as *const Bar; + + vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>(); +} diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr new file mode 100644 index 00000000000..7fd10f3cb68 --- /dev/null +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -0,0 +1,240 @@ +error: casting `*const U` as `*const V` is invalid + --> $DIR/cast-rfc0401.rs:13:5 + | +13 | u as *const V + | ^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error: casting `*const U` as `*const str` is invalid + --> $DIR/cast-rfc0401.rs:18:5 + | +18 | u as *const str + | ^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error: no field `f` on type `fn() {main}` + --> $DIR/cast-rfc0401.rs:75:18 + | +75 | let _ = main.f as *const u32; + | ^ + +error: non-scalar cast: `*const u8` as `&u8` + --> $DIR/cast-rfc0401.rs:39:13 + | +39 | let _ = v as &u8; + | ^^^^^^^^ + +error: non-scalar cast: `*const u8` as `E` + --> $DIR/cast-rfc0401.rs:40:13 + | +40 | let _ = v as E; + | ^^^^^^ + +error: non-scalar cast: `*const u8` as `fn()` + --> $DIR/cast-rfc0401.rs:41:13 + | +41 | let _ = v as fn(); + | ^^^^^^^^^ + +error: non-scalar cast: `*const u8` as `(u32,)` + --> $DIR/cast-rfc0401.rs:42:13 + | +42 | let _ = v as (u32,); + | ^^^^^^^^^^^ + +error: non-scalar cast: `std::option::Option<&*const u8>` as `*const u8` + --> $DIR/cast-rfc0401.rs:43:13 + | +43 | let _ = Some(&v) as *const u8; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: casting `*const u8` as `f32` is invalid + --> $DIR/cast-rfc0401.rs:45:13 + | +45 | let _ = v as f32; + | ^^^^^^^^ + +error: casting `fn() {main}` as `f64` is invalid + --> $DIR/cast-rfc0401.rs:46:13 + | +46 | let _ = main as f64; + | ^^^^^^^^^^^ + +error: casting `&*const u8` as `usize` is invalid + --> $DIR/cast-rfc0401.rs:47:13 + | +47 | let _ = &v as usize; + | ^^^^^^^^^^^ + | + = help: cast through a raw pointer first + +error: casting `f32` as `*const u8` is invalid + --> $DIR/cast-rfc0401.rs:48:13 + | +48 | let _ = f as *const u8; + | ^^^^^^^^^^^^^^ + +error[E0054]: cannot cast as `bool` + --> $DIR/cast-rfc0401.rs:49:13 + | +49 | let _ = 3_i32 as bool; + | ^^^^^^^^^^^^^ unsupported cast + | + = help: compare with zero instead + +error[E0054]: cannot cast as `bool` + --> $DIR/cast-rfc0401.rs:50:13 + | +50 | let _ = E::A as bool; + | ^^^^^^^^^^^^ unsupported cast + | + = help: compare with zero instead + +error: only `u8` can be cast as `char`, not `u32` + --> $DIR/cast-rfc0401.rs:51:13 + | +51 | let _ = 0x61u32 as char; + | ^^^^^^^^^^^^^^^ + +error: casting `bool` as `f32` is invalid + --> $DIR/cast-rfc0401.rs:53:13 + | +53 | let _ = false as f32; + | ^^^^^^^^^^^^ + | + = help: cast through an integer first + +error: casting `E` as `f32` is invalid + --> $DIR/cast-rfc0401.rs:54:13 + | +54 | let _ = E::A as f32; + | ^^^^^^^^^^^ + | + = help: cast through an integer first + +error: casting `char` as `f32` is invalid + --> $DIR/cast-rfc0401.rs:55:13 + | +55 | let _ = 'a' as f32; + | ^^^^^^^^^^ + | + = help: cast through an integer first + +error: casting `bool` as `*const u8` is invalid + --> $DIR/cast-rfc0401.rs:57:13 + | +57 | let _ = false as *const u8; + | ^^^^^^^^^^^^^^^^^^ + +error: casting `E` as `*const u8` is invalid + --> $DIR/cast-rfc0401.rs:58:13 + | +58 | let _ = E::A as *const u8; + | ^^^^^^^^^^^^^^^^^ + +error: casting `char` as `*const u8` is invalid + --> $DIR/cast-rfc0401.rs:59:13 + | +59 | let _ = 'a' as *const u8; + | ^^^^^^^^^^^^^^^^ + +error: casting `usize` as `*const [u8]` is invalid + --> $DIR/cast-rfc0401.rs:61:13 + | +61 | let _ = 42usize as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]` + --> $DIR/cast-rfc0401.rs:62:13 + | +62 | let _ = v as *const [u8]; + | ^^^^^^^^^^^^^^^^ + +error: casting `&Foo` as `*const str` is invalid + --> $DIR/cast-rfc0401.rs:64:13 + | +64 | let _ = foo as *const str; + | ^^^^^^^^^^^^^^^^^ + +error: casting `&Foo` as `*mut str` is invalid + --> $DIR/cast-rfc0401.rs:65:13 + | +65 | let _ = foo as *mut str; + | ^^^^^^^^^^^^^^^ + +error: casting `fn() {main}` as `*mut str` is invalid + --> $DIR/cast-rfc0401.rs:66:13 + | +66 | let _ = main as *mut str; + | ^^^^^^^^^^^^^^^^ + +error: casting `&f32` as `*mut f32` is invalid + --> $DIR/cast-rfc0401.rs:67:13 + | +67 | let _ = &f as *mut f32; + | ^^^^^^^^^^^^^^ + +error: casting `&f32` as `*const f64` is invalid + --> $DIR/cast-rfc0401.rs:68:13 + | +68 | let _ = &f as *const f64; + | ^^^^^^^^^^^^^^^^ + +error: casting `*const [i8]` as `usize` is invalid + --> $DIR/cast-rfc0401.rs:69:13 + | +69 | let _ = fat_sv as usize; + | ^^^^^^^^^^^^^^^ + | + = help: cast through a thin pointer first + +error: casting `*const Foo` as `*const [u16]` is invalid + --> $DIR/cast-rfc0401.rs:78:13 + | +78 | let _ = cf as *const [u16]; + | ^^^^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error: casting `*const Foo` as `*const Bar` is invalid + --> $DIR/cast-rfc0401.rs:79:13 + | +79 | let _ = cf as *const Bar; + | ^^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied + --> $DIR/cast-rfc0401.rs:63:13 + | +63 | let _ = fat_v as *const Foo; + | ^^^^^ the trait `std::marker::Sized` is not implemented for `[u8]` + | + = note: `[u8]` does not have a constant size known at compile-time + = note: required for the cast to the object type `Foo` + +error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied + --> $DIR/cast-rfc0401.rs:72:13 + | +72 | let _ = a as *const Foo; + | ^ the trait `std::marker::Sized` is not implemented for `str` + | + = note: `str` does not have a constant size known at compile-time + = note: required for the cast to the object type `Foo` + +error: casting `&{float}` as `f32` is invalid + --> $DIR/cast-rfc0401.rs:81:30 + | +81 | vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>(); + | ^^^^^^^^ cannot cast `&{float}` as `f32` + | +help: did you mean `*s`? + --> $DIR/cast-rfc0401.rs:81:30 + | +81 | vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>(); + | ^ + +error: aborting due to 34 previous errors + diff --git a/src/test/compile-fail/auxiliary/issue-21221-3.rs b/src/test/ui/resolve/auxiliary/issue-21221-3.rs index fae0fe16a26..fae0fe16a26 100644 --- a/src/test/compile-fail/auxiliary/issue-21221-3.rs +++ b/src/test/ui/resolve/auxiliary/issue-21221-3.rs diff --git a/src/test/compile-fail/auxiliary/issue-21221-4.rs b/src/test/ui/resolve/auxiliary/issue-21221-4.rs index fffe060ee24..fffe060ee24 100644 --- a/src/test/compile-fail/auxiliary/issue-21221-4.rs +++ b/src/test/ui/resolve/auxiliary/issue-21221-4.rs diff --git a/src/test/compile-fail/auxiliary/issue_19452_aux.rs b/src/test/ui/resolve/auxiliary/issue_19452_aux.rs index 205566e4b1f..205566e4b1f 100644 --- a/src/test/compile-fail/auxiliary/issue_19452_aux.rs +++ b/src/test/ui/resolve/auxiliary/issue_19452_aux.rs diff --git a/src/test/compile-fail/auxiliary/issue_3907.rs b/src/test/ui/resolve/auxiliary/issue_3907.rs index 6472c08c222..6472c08c222 100644 --- a/src/test/compile-fail/auxiliary/issue_3907.rs +++ b/src/test/ui/resolve/auxiliary/issue_3907.rs diff --git a/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs b/src/test/ui/resolve/auxiliary/namespaced_enums.rs index 465bddd060d..3bf39b788db 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs +++ b/src/test/ui/resolve/auxiliary/namespaced_enums.rs @@ -8,10 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn f<F:Nonexist(isize) -> isize>(x: F) {} //~ ERROR trait `Nonexist` is not in scope +pub enum Foo { + A, + B(isize), + C { a: isize }, +} -type Typedef = isize; - -fn g<F:Typedef(isize) -> isize>(x: F) {} //~ ERROR `Typedef` is not a trait - -fn main() {} +impl Foo { + pub fn foo() {} + pub fn bar(&self) {} +} diff --git a/src/test/compile-fail/enums-are-namespaced-xc.rs b/src/test/ui/resolve/enums-are-namespaced-xc.rs index 02939565f69..4f55f33d7f8 100644 --- a/src/test/compile-fail/enums-are-namespaced-xc.rs +++ b/src/test/ui/resolve/enums-are-namespaced-xc.rs @@ -12,8 +12,13 @@ extern crate namespaced_enums; fn main() { - let _ = namespaced_enums::A; //~ ERROR unresolved name - let _ = namespaced_enums::B(10); //~ ERROR unresolved name + let _ = namespaced_enums::A; + //~^ ERROR unresolved value `namespaced_enums::A` + //~| HELP you can import it into scope: `use namespaced_enums::Foo::A;` + let _ = namespaced_enums::B(10); + //~^ ERROR unresolved function `namespaced_enums::B` + //~| HELP you can import it into scope: `use namespaced_enums::Foo::B;` let _ = namespaced_enums::C { a: 10 }; //~^ ERROR unresolved struct, variant or union type `namespaced_enums::C` + //~| HELP you can import it into scope: `use namespaced_enums::Foo::C;` } diff --git a/src/test/ui/resolve/enums-are-namespaced-xc.stderr b/src/test/ui/resolve/enums-are-namespaced-xc.stderr new file mode 100644 index 00000000000..fda8023b7f6 --- /dev/null +++ b/src/test/ui/resolve/enums-are-namespaced-xc.stderr @@ -0,0 +1,29 @@ +error[E0425]: unresolved value `namespaced_enums::A` + --> $DIR/enums-are-namespaced-xc.rs:15:13 + | +15 | let _ = namespaced_enums::A; + | ^^^^^^^^^^^^^^^^^^^ no resolution found + | + = help: possible candidate is found in another module, you can import it into scope: + = help: `use namespaced_enums::Foo::A;` + +error[E0425]: unresolved function `namespaced_enums::B` + --> $DIR/enums-are-namespaced-xc.rs:18:13 + | +18 | let _ = namespaced_enums::B(10); + | ^^^^^^^^^^^^^^^^^^^ no resolution found + | + = help: possible candidate is found in another module, you can import it into scope: + = help: `use namespaced_enums::Foo::B;` + +error[E0422]: unresolved struct, variant or union type `namespaced_enums::C` + --> $DIR/enums-are-namespaced-xc.rs:21:13 + | +21 | let _ = namespaced_enums::C { a: 10 }; + | ^^^^^^^^^^^^^^^^^^^ no resolution found + | + = help: possible candidate is found in another module, you can import it into scope: + = help: `use namespaced_enums::Foo::C;` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/resolve/issue-14254.rs b/src/test/ui/resolve/issue-14254.rs new file mode 100644 index 00000000000..b1fc6c47720 --- /dev/null +++ b/src/test/ui/resolve/issue-14254.rs @@ -0,0 +1,137 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + fn bar(&self); + fn baz(&self) { } + fn bah(_: Option<&Self>) { } +} + +struct BarTy { + x : isize, + y : f64, +} + +impl BarTy { + fn a() {} + fn b(&self) {} +} + +impl Foo for *const BarTy { + fn bar(&self) { + baz(); + //~^ ERROR unresolved function `baz` + //~| NOTE did you mean `self.baz(...)`? + a; + //~^ ERROR unresolved value `a` + //~| NOTE no resolution found + } +} + +impl<'a> Foo for &'a BarTy { + fn bar(&self) { + baz(); + //~^ ERROR unresolved function `baz` + //~| NOTE did you mean `self.baz(...)`? + x; + //~^ ERROR unresolved value `x` + //~| NOTE did you mean `self.x`? + y; + //~^ ERROR unresolved value `y` + //~| NOTE did you mean `self.y`? + a; + //~^ ERROR unresolved value `a` + //~| NOTE no resolution found + bah; + //~^ ERROR unresolved value `bah` + //~| NOTE did you mean `Self::bah`? + b; + //~^ ERROR unresolved value `b` + //~| NOTE no resolution found + } +} + +impl<'a> Foo for &'a mut BarTy { + fn bar(&self) { + baz(); + //~^ ERROR unresolved function `baz` + //~| NOTE did you mean `self.baz(...)`? + x; + //~^ ERROR unresolved value `x` + //~| NOTE did you mean `self.x`? + y; + //~^ ERROR unresolved value `y` + //~| NOTE did you mean `self.y`? + a; + //~^ ERROR unresolved value `a` + //~| NOTE no resolution found + bah; + //~^ ERROR unresolved value `bah` + //~| NOTE did you mean `Self::bah`? + b; + //~^ ERROR unresolved value `b` + //~| NOTE no resolution found + } +} + +impl Foo for Box<BarTy> { + fn bar(&self) { + baz(); + //~^ ERROR unresolved function `baz` + //~| NOTE did you mean `self.baz(...)`? + bah; + //~^ ERROR unresolved value `bah` + //~| NOTE did you mean `Self::bah`? + } +} + +impl Foo for *const isize { + fn bar(&self) { + baz(); + //~^ ERROR unresolved function `baz` + //~| NOTE did you mean `self.baz(...)`? + bah; + //~^ ERROR unresolved value `bah` + //~| NOTE did you mean `Self::bah`? + } +} + +impl<'a> Foo for &'a isize { + fn bar(&self) { + baz(); + //~^ ERROR unresolved function `baz` + //~| NOTE did you mean `self.baz(...)`? + bah; + //~^ ERROR unresolved value `bah` + //~| NOTE did you mean `Self::bah`? + } +} + +impl<'a> Foo for &'a mut isize { + fn bar(&self) { + baz(); + //~^ ERROR unresolved function `baz` + //~| NOTE did you mean `self.baz(...)`? + bah; + //~^ ERROR unresolved value `bah` + //~| NOTE did you mean `Self::bah`? + } +} + +impl Foo for Box<isize> { + fn bar(&self) { + baz(); + //~^ ERROR unresolved function `baz` + //~| NOTE did you mean `self.baz(...)`? + bah; + //~^ ERROR unresolved value `bah` + //~| NOTE did you mean `Self::bah`? + } +} diff --git a/src/test/ui/resolve/issue-14254.stderr b/src/test/ui/resolve/issue-14254.stderr new file mode 100644 index 00000000000..2780c1c406f --- /dev/null +++ b/src/test/ui/resolve/issue-14254.stderr @@ -0,0 +1,148 @@ +error[E0425]: unresolved function `baz` + --> $DIR/issue-14254.rs:29:9 + | +29 | baz(); + | ^^^ did you mean `self.baz(...)`? + +error[E0425]: unresolved value `a` + --> $DIR/issue-14254.rs:32:9 + | +32 | a; + | ^ no resolution found + +error[E0425]: unresolved function `baz` + --> $DIR/issue-14254.rs:40:9 + | +40 | baz(); + | ^^^ did you mean `self.baz(...)`? + +error[E0425]: unresolved value `x` + --> $DIR/issue-14254.rs:43:9 + | +43 | x; + | ^ did you mean `self.x`? + +error[E0425]: unresolved value `y` + --> $DIR/issue-14254.rs:46:9 + | +46 | y; + | ^ did you mean `self.y`? + +error[E0425]: unresolved value `a` + --> $DIR/issue-14254.rs:49:9 + | +49 | a; + | ^ no resolution found + +error[E0425]: unresolved value `bah` + --> $DIR/issue-14254.rs:52:9 + | +52 | bah; + | ^^^ did you mean `Self::bah`? + +error[E0425]: unresolved value `b` + --> $DIR/issue-14254.rs:55:9 + | +55 | b; + | ^ no resolution found + +error[E0425]: unresolved function `baz` + --> $DIR/issue-14254.rs:63:9 + | +63 | baz(); + | ^^^ did you mean `self.baz(...)`? + +error[E0425]: unresolved value `x` + --> $DIR/issue-14254.rs:66:9 + | +66 | x; + | ^ did you mean `self.x`? + +error[E0425]: unresolved value `y` + --> $DIR/issue-14254.rs:69:9 + | +69 | y; + | ^ did you mean `self.y`? + +error[E0425]: unresolved value `a` + --> $DIR/issue-14254.rs:72:9 + | +72 | a; + | ^ no resolution found + +error[E0425]: unresolved value `bah` + --> $DIR/issue-14254.rs:75:9 + | +75 | bah; + | ^^^ did you mean `Self::bah`? + +error[E0425]: unresolved value `b` + --> $DIR/issue-14254.rs:78:9 + | +78 | b; + | ^ no resolution found + +error[E0425]: unresolved function `baz` + --> $DIR/issue-14254.rs:86:9 + | +86 | baz(); + | ^^^ did you mean `self.baz(...)`? + +error[E0425]: unresolved value `bah` + --> $DIR/issue-14254.rs:89:9 + | +89 | bah; + | ^^^ did you mean `Self::bah`? + +error[E0425]: unresolved function `baz` + --> $DIR/issue-14254.rs:97:9 + | +97 | baz(); + | ^^^ did you mean `self.baz(...)`? + +error[E0425]: unresolved value `bah` + --> $DIR/issue-14254.rs:100:9 + | +100 | bah; + | ^^^ did you mean `Self::bah`? + +error[E0425]: unresolved function `baz` + --> $DIR/issue-14254.rs:108:9 + | +108 | baz(); + | ^^^ did you mean `self.baz(...)`? + +error[E0425]: unresolved value `bah` + --> $DIR/issue-14254.rs:111:9 + | +111 | bah; + | ^^^ did you mean `Self::bah`? + +error[E0425]: unresolved function `baz` + --> $DIR/issue-14254.rs:119:9 + | +119 | baz(); + | ^^^ did you mean `self.baz(...)`? + +error[E0425]: unresolved value `bah` + --> $DIR/issue-14254.rs:122:9 + | +122 | bah; + | ^^^ did you mean `Self::bah`? + +error[E0425]: unresolved function `baz` + --> $DIR/issue-14254.rs:130:9 + | +130 | baz(); + | ^^^ did you mean `self.baz(...)`? + +error[E0425]: unresolved value `bah` + --> $DIR/issue-14254.rs:133:9 + | +133 | bah; + | ^^^ did you mean `Self::bah`? + +error: main function not found + +error: aborting due to 25 previous errors + diff --git a/src/test/compile-fail/issue-16058.rs b/src/test/ui/resolve/issue-16058.rs index 92c1e4b5f50..1f777e53632 100644 --- a/src/test/compile-fail/issue-16058.rs +++ b/src/test/ui/resolve/issue-16058.rs @@ -16,7 +16,12 @@ pub struct GslResult { impl GslResult { pub fn new() -> GslResult { - Result { //~ ERROR: expected struct, variant or union type, found enum `Result` + Result { +//~^ ERROR expected struct, variant or union type, found enum `Result` +//~| HELP possible better candidates are found in other modules, you can import them into scope +//~| HELP std::fmt::Result +//~| HELP std::io::Result +//~| HELP std::thread::Result val: 0f64, err: 0f64 } diff --git a/src/test/ui/resolve/issue-16058.stderr b/src/test/ui/resolve/issue-16058.stderr new file mode 100644 index 00000000000..6b00cfb8693 --- /dev/null +++ b/src/test/ui/resolve/issue-16058.stderr @@ -0,0 +1,13 @@ +error[E0574]: expected struct, variant or union type, found enum `Result` + --> $DIR/issue-16058.rs:19:9 + | +19 | Result { + | ^^^^^^ not a struct, variant or union type + | + = help: possible better candidates are found in other modules, you can import them into scope: + = help: `use std::fmt::Result;` + = help: `use std::io::Result;` + = help: `use std::thread::Result;` + +error: aborting due to previous error + diff --git a/src/test/compile-fail/issue-17518.rs b/src/test/ui/resolve/issue-17518.rs index 2113e38c45c..3ac9b379d18 100644 --- a/src/test/compile-fail/issue-17518.rs +++ b/src/test/ui/resolve/issue-17518.rs @@ -14,4 +14,5 @@ enum SomeEnum { fn main() { E { name: "foobar" }; //~ ERROR unresolved struct, variant or union type `E` + //~^ HELP you can import it into scope: `use SomeEnum::E;` } diff --git a/src/test/ui/resolve/issue-17518.stderr b/src/test/ui/resolve/issue-17518.stderr new file mode 100644 index 00000000000..9de43728b2f --- /dev/null +++ b/src/test/ui/resolve/issue-17518.stderr @@ -0,0 +1,11 @@ +error[E0422]: unresolved struct, variant or union type `E` + --> $DIR/issue-17518.rs:16:5 + | +16 | E { name: "foobar" }; //~ ERROR unresolved struct, variant or union type `E` + | ^ no resolution found + | + = help: possible candidate is found in another module, you can import it into scope: + = help: `use SomeEnum::E;` + +error: aborting due to previous error + diff --git a/src/test/compile-fail/issue-18252.rs b/src/test/ui/resolve/issue-18252.rs index 8e3faca02b7..02c6643a920 100644 --- a/src/test/compile-fail/issue-18252.rs +++ b/src/test/ui/resolve/issue-18252.rs @@ -14,6 +14,5 @@ enum Foo { fn main() { let f = Foo::Variant(42); - //~^ ERROR uses it like a function - //~| struct called like a function + //~^ ERROR expected function, found struct variant `Foo::Variant` } diff --git a/src/test/ui/resolve/issue-18252.stderr b/src/test/ui/resolve/issue-18252.stderr new file mode 100644 index 00000000000..edc7196d846 --- /dev/null +++ b/src/test/ui/resolve/issue-18252.stderr @@ -0,0 +1,8 @@ +error[E0423]: expected function, found struct variant `Foo::Variant` + --> $DIR/issue-18252.rs:16:13 + | +16 | let f = Foo::Variant(42); + | ^^^^^^^^^^^^ did you mean `Foo::Variant { /* fields */ }`? + +error: aborting due to previous error + diff --git a/src/test/compile-fail/issue-19452.rs b/src/test/ui/resolve/issue-19452.rs index 34872b7c8c5..080fb064c9f 100644 --- a/src/test/compile-fail/issue-19452.rs +++ b/src/test/ui/resolve/issue-19452.rs @@ -17,10 +17,8 @@ enum Homura { fn main() { let homura = Homura::Madoka; - //~^ ERROR uses it like a function - //~| struct called like a function + //~^ ERROR expected value, found struct variant `Homura::Madoka` let homura = issue_19452_aux::Homura::Madoka; - //~^ ERROR uses it like a function - //~| struct called like a function + //~^ ERROR expected value, found struct variant `issue_19452_aux::Homura::Madoka` } diff --git a/src/test/ui/resolve/issue-19452.stderr b/src/test/ui/resolve/issue-19452.stderr new file mode 100644 index 00000000000..7b14d49af51 --- /dev/null +++ b/src/test/ui/resolve/issue-19452.stderr @@ -0,0 +1,14 @@ +error[E0423]: expected value, found struct variant `Homura::Madoka` + --> $DIR/issue-19452.rs:19:18 + | +19 | let homura = Homura::Madoka; + | ^^^^^^^^^^^^^^ did you mean `Homura::Madoka { /* fields */ }`? + +error[E0423]: expected value, found struct variant `issue_19452_aux::Homura::Madoka` + --> $DIR/issue-19452.rs:22:18 + | +22 | let homura = issue_19452_aux::Homura::Madoka; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ did you mean `issue_19452_aux::Homura::Madoka { /* fields */ }`? + +error: aborting due to 2 previous errors + diff --git a/src/test/compile-fail/issue-21221-1.rs b/src/test/ui/resolve/issue-21221-1.rs index 2bc9ec3289a..b1266a5af35 100644 --- a/src/test/compile-fail/issue-21221-1.rs +++ b/src/test/ui/resolve/issue-21221-1.rs @@ -51,11 +51,11 @@ struct Foo; // help: `std::ops::Mul` impl Mul for Foo { -//~^ ERROR trait `Mul` is not in scope +//~^ ERROR unresolved trait `Mul` +//~| HELP possible candidates are found in other modules, you can import them into scope //~| HELP `mul1::Mul` //~| HELP `mul2::Mul` //~| HELP `std::ops::Mul` -//~| HELP you can import several candidates into scope (`use ...;`): } // BEFORE, we got: @@ -70,24 +70,23 @@ impl Mul for Foo { // help: `mul4::Mul` // help: and 2 other candidates fn getMul() -> Mul { -//~^ ERROR type name `Mul` is undefined or not in scope +//~^ ERROR unresolved type `Mul` +//~| HELP possible candidates are found in other modules, you can import them into scope //~| HELP `mul1::Mul` //~| HELP `mul2::Mul` //~| HELP `mul3::Mul` //~| HELP `mul4::Mul` //~| HELP and 2 other candidates -//~| HELP you can import several candidates into scope (`use ...;`): } // Let's also test what happens if the trait doesn't exist: impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo { -//~^ ERROR trait `ThisTraitReallyDoesntExistInAnyModuleReally` is not in scope -//~| HELP no candidates by the name of `ThisTraitReallyDoesntExistInAnyModuleReally` found +//~^ ERROR unresolved trait `ThisTraitReallyDoesntExistInAnyModuleReally` } // Let's also test what happens if there's just one alternative: impl Div for Foo { -//~^ ERROR trait `Div` is not in scope +//~^ ERROR unresolved trait `Div` //~| HELP `use std::ops::Div;` } diff --git a/src/test/ui/resolve/issue-21221-1.stderr b/src/test/ui/resolve/issue-21221-1.stderr new file mode 100644 index 00000000000..17b70d2182e --- /dev/null +++ b/src/test/ui/resolve/issue-21221-1.stderr @@ -0,0 +1,41 @@ +error[E0405]: unresolved trait `Mul` + --> $DIR/issue-21221-1.rs:53:6 + | +53 | impl Mul for Foo { + | ^^^ no resolution found + | + = help: possible candidates are found in other modules, you can import them into scope: + = help: `use mul1::Mul;` + = help: `use mul2::Mul;` + = help: `use std::ops::Mul;` + +error[E0412]: unresolved type `Mul` + --> $DIR/issue-21221-1.rs:72:16 + | +72 | fn getMul() -> Mul { + | ^^^ no resolution found + | + = help: possible candidates are found in other modules, you can import them into scope: + = help: `use mul1::Mul;` + = help: `use mul2::Mul;` + = help: `use mul3::Mul;` + = help: `use mul4::Mul;` + = help: and 2 other candidates + +error[E0405]: unresolved trait `ThisTraitReallyDoesntExistInAnyModuleReally` + --> $DIR/issue-21221-1.rs:83:6 + | +83 | impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no resolution found + +error[E0405]: unresolved trait `Div` + --> $DIR/issue-21221-1.rs:88:6 + | +88 | impl Div for Foo { + | ^^^ no resolution found + | + = help: possible candidate is found in another module, you can import it into scope: + = help: `use std::ops::Div;` + +error: cannot continue compilation due to previous error + diff --git a/src/test/compile-fail/issue-21221-2.rs b/src/test/ui/resolve/issue-21221-2.rs index 861acf62d0b..15e859329c4 100644 --- a/src/test/compile-fail/issue-21221-2.rs +++ b/src/test/ui/resolve/issue-21221-2.rs @@ -26,5 +26,5 @@ pub mod baz { struct Foo; impl T for Foo { } -//~^ ERROR trait `T` is not in scope -//~| HELP you can import it into scope: `use foo::bar::T;`. +//~^ ERROR unresolved trait `T` +//~| HELP you can import it into scope: `use foo::bar::T;` diff --git a/src/test/ui/resolve/issue-21221-2.stderr b/src/test/ui/resolve/issue-21221-2.stderr new file mode 100644 index 00000000000..342fe12f5e5 --- /dev/null +++ b/src/test/ui/resolve/issue-21221-2.stderr @@ -0,0 +1,13 @@ +error[E0405]: unresolved trait `T` + --> $DIR/issue-21221-2.rs:28:6 + | +28 | impl T for Foo { } + | ^ no resolution found + | + = help: possible candidate is found in another module, you can import it into scope: + = help: `use foo::bar::T;` + +error: main function not found + +error: cannot continue compilation due to previous error + diff --git a/src/test/compile-fail/issue-21221-3.rs b/src/test/ui/resolve/issue-21221-3.rs index 05786e69cef..5d62cb85914 100644 --- a/src/test/compile-fail/issue-21221-3.rs +++ b/src/test/ui/resolve/issue-21221-3.rs @@ -23,8 +23,8 @@ struct Foo; // `issue_21221_3::outer::public_module::OuterTrait` // are hidden from the view. impl OuterTrait for Foo {} -//~^ ERROR trait `OuterTrait` is not in scope -//~| HELP you can import it into scope: `use issue_21221_3::outer::OuterTrait;`. +//~^ ERROR unresolved trait `OuterTrait` +//~| HELP you can import it into scope: `use issue_21221_3::outer::OuterTrait;` fn main() { println!("Hello, world!"); } diff --git a/src/test/ui/resolve/issue-21221-3.stderr b/src/test/ui/resolve/issue-21221-3.stderr new file mode 100644 index 00000000000..25f57f3e3d1 --- /dev/null +++ b/src/test/ui/resolve/issue-21221-3.stderr @@ -0,0 +1,11 @@ +error[E0405]: unresolved trait `OuterTrait` + --> $DIR/issue-21221-3.rs:25:6 + | +25 | impl OuterTrait for Foo {} + | ^^^^^^^^^^ no resolution found + | + = help: possible candidate is found in another module, you can import it into scope: + = help: `use issue_21221_3::outer::OuterTrait;` + +error: cannot continue compilation due to previous error + diff --git a/src/test/compile-fail/issue-21221-4.rs b/src/test/ui/resolve/issue-21221-4.rs index bcbee16cdcf..ff6698f8717 100644 --- a/src/test/compile-fail/issue-21221-4.rs +++ b/src/test/ui/resolve/issue-21221-4.rs @@ -18,8 +18,8 @@ extern crate issue_21221_4; struct Foo; impl T for Foo {} -//~^ ERROR trait `T` is not in scope -//~| HELP you can import it into scope: `use issue_21221_4::T;`. +//~^ ERROR unresolved trait `T` +//~| HELP you can import it into scope: `use issue_21221_4::T;` fn main() { println!("Hello, world!"); diff --git a/src/test/ui/resolve/issue-21221-4.stderr b/src/test/ui/resolve/issue-21221-4.stderr new file mode 100644 index 00000000000..7ddbeebb8df --- /dev/null +++ b/src/test/ui/resolve/issue-21221-4.stderr @@ -0,0 +1,11 @@ +error[E0405]: unresolved trait `T` + --> $DIR/issue-21221-4.rs:20:6 + | +20 | impl T for Foo {} + | ^ no resolution found + | + = help: possible candidate is found in another module, you can import it into scope: + = help: `use issue_21221_4::T;` + +error: cannot continue compilation due to previous error + diff --git a/src/test/ui/resolve/issue-23305.rs b/src/test/ui/resolve/issue-23305.rs new file mode 100644 index 00000000000..19069f49167 --- /dev/null +++ b/src/test/ui/resolve/issue-23305.rs @@ -0,0 +1,22 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub trait ToNbt<T> { + fn new(val: T) -> Self; +} + +impl ToNbt<Self> {} +//~^ ERROR unresolved type `Self` +//~| NOTE `Self` is only available in traits and impls +//~| ERROR the trait `ToNbt` cannot be made into an object +//~| NOTE the trait `ToNbt` cannot be made into an object +//~| NOTE method `new` has no receiver + +fn main() {} diff --git a/src/test/ui/resolve/issue-23305.stderr b/src/test/ui/resolve/issue-23305.stderr new file mode 100644 index 00000000000..cd0d7d3ce16 --- /dev/null +++ b/src/test/ui/resolve/issue-23305.stderr @@ -0,0 +1,16 @@ +error[E0411]: unresolved type `Self` + --> $DIR/issue-23305.rs:15:12 + | +15 | impl ToNbt<Self> {} + | ^^^^ `Self` is only available in traits and impls + +error[E0038]: the trait `ToNbt` cannot be made into an object + --> $DIR/issue-23305.rs:15:6 + | +15 | impl ToNbt<Self> {} + | ^^^^^^^^^^^ the trait `ToNbt` cannot be made into an object + | + = note: method `new` has no receiver + +error: aborting due to previous error + diff --git a/src/test/ui/resolve/issue-2356.rs b/src/test/ui/resolve/issue-2356.rs new file mode 100644 index 00000000000..6deb598b631 --- /dev/null +++ b/src/test/ui/resolve/issue-2356.rs @@ -0,0 +1,125 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Groom { + fn shave(other: usize); +} + +pub struct cat { + whiskers: isize, +} + +pub enum MaybeDog { + Dog, + NoDog +} + +impl MaybeDog { + fn bark() { + // If this provides a suggestion, it's a bug as MaybeDog doesn't impl Groom + shave(); + //~^ ERROR unresolved function `shave` + //~| NOTE no resolution found + } +} + +impl Clone for cat { + fn clone(&self) -> Self { + clone(); + //~^ ERROR unresolved function `clone` + //~| NOTE did you mean `self.clone(...)`? + loop {} + } +} +impl Default for cat { + fn default() -> Self { + default(); + //~^ ERROR unresolved function `default` + //~| NOTE did you mean `Self::default`? + loop {} + } +} + +impl Groom for cat { + fn shave(other: usize) { + whiskers -= other; + //~^ ERROR unresolved value `whiskers` + //~| ERROR unresolved value `whiskers` + //~| NOTE did you mean `self.whiskers`? + //~| NOTE `self` value is only available in methods with `self` parameter + shave(4); + //~^ ERROR unresolved function `shave` + //~| NOTE did you mean `Self::shave`? + purr(); + //~^ ERROR unresolved function `purr` + //~| NOTE no resolution found + } +} + +impl cat { + fn static_method() {} + + fn purr_louder() { + static_method(); + //~^ ERROR unresolved function `static_method` + //~| NOTE no resolution found + purr(); + //~^ ERROR unresolved function `purr` + //~| NOTE no resolution found + purr(); + //~^ ERROR unresolved function `purr` + //~| NOTE no resolution found + purr(); + //~^ ERROR unresolved function `purr` + //~| NOTE no resolution found + } +} + +impl cat { + fn meow() { + if self.whiskers > 3 { + //~^ ERROR expected value, found module `self` + //~| NOTE `self` value is only available in methods with `self` parameter + println!("MEOW"); + } + } + + fn purr(&self) { + grow_older(); + //~^ ERROR unresolved function `grow_older` + //~| NOTE no resolution found + shave(); + //~^ ERROR unresolved function `shave` + //~| NOTE no resolution found + } + + fn burn_whiskers(&mut self) { + whiskers = 0; + //~^ ERROR unresolved value `whiskers` + //~| NOTE did you mean `self.whiskers`? + } + + pub fn grow_older(other:usize) { + whiskers = 4; + //~^ ERROR unresolved value `whiskers` + //~| ERROR unresolved value `whiskers` + //~| NOTE did you mean `self.whiskers`? + //~| NOTE `self` value is only available in methods with `self` parameter + purr_louder(); + //~^ ERROR unresolved function `purr_louder` + //~| NOTE no resolution found + } +} + +fn main() { + self += 1; + //~^ ERROR expected value, found module `self` + //~| NOTE `self` value is only available in methods with `self` parameter +} diff --git a/src/test/ui/resolve/issue-2356.stderr b/src/test/ui/resolve/issue-2356.stderr new file mode 100644 index 00000000000..32c1a3694d7 --- /dev/null +++ b/src/test/ui/resolve/issue-2356.stderr @@ -0,0 +1,110 @@ +error[E0425]: unresolved function `shave` + --> $DIR/issue-2356.rs:27:5 + | +27 | shave(); + | ^^^^^ no resolution found + +error[E0425]: unresolved function `clone` + --> $DIR/issue-2356.rs:35:5 + | +35 | clone(); + | ^^^^^ did you mean `self.clone(...)`? + +error[E0425]: unresolved function `default` + --> $DIR/issue-2356.rs:43:5 + | +43 | default(); + | ^^^^^^^ did you mean `Self::default`? + +error[E0425]: unresolved value `whiskers` + --> $DIR/issue-2356.rs:52:5 + | +52 | whiskers -= other; + | ^^^^^^^^ + | | + | did you mean `self.whiskers`? + | `self` value is only available in methods with `self` parameter + +error[E0425]: unresolved function `shave` + --> $DIR/issue-2356.rs:57:5 + | +57 | shave(4); + | ^^^^^ did you mean `Self::shave`? + +error[E0425]: unresolved function `purr` + --> $DIR/issue-2356.rs:60:5 + | +60 | purr(); + | ^^^^ no resolution found + +error[E0425]: unresolved function `static_method` + --> $DIR/issue-2356.rs:70:9 + | +70 | static_method(); + | ^^^^^^^^^^^^^ no resolution found + +error[E0425]: unresolved function `purr` + --> $DIR/issue-2356.rs:73:9 + | +73 | purr(); + | ^^^^ no resolution found + +error[E0425]: unresolved function `purr` + --> $DIR/issue-2356.rs:76:9 + | +76 | purr(); + | ^^^^ no resolution found + +error[E0425]: unresolved function `purr` + --> $DIR/issue-2356.rs:79:9 + | +79 | purr(); + | ^^^^ no resolution found + +error[E0424]: expected value, found module `self` + --> $DIR/issue-2356.rs:87:8 + | +87 | if self.whiskers > 3 { + | ^^^^ `self` value is only available in methods with `self` parameter + +error[E0425]: unresolved function `grow_older` + --> $DIR/issue-2356.rs:95:5 + | +95 | grow_older(); + | ^^^^^^^^^^ no resolution found + +error[E0425]: unresolved function `shave` + --> $DIR/issue-2356.rs:98:5 + | +98 | shave(); + | ^^^^^ no resolution found + +error[E0425]: unresolved value `whiskers` + --> $DIR/issue-2356.rs:104:5 + | +104 | whiskers = 0; + | ^^^^^^^^ did you mean `self.whiskers`? + +error[E0425]: unresolved value `whiskers` + --> $DIR/issue-2356.rs:110:5 + | +110 | whiskers = 4; + | ^^^^^^^^ + | | + | did you mean `self.whiskers`? + | `self` value is only available in methods with `self` parameter + +error[E0425]: unresolved function `purr_louder` + --> $DIR/issue-2356.rs:115:5 + | +115 | purr_louder(); + | ^^^^^^^^^^^ no resolution found + +error[E0424]: expected value, found module `self` + --> $DIR/issue-2356.rs:122:5 + | +122 | self += 1; + | ^^^^ `self` value is only available in methods with `self` parameter + +error: aborting due to 17 previous errors + diff --git a/src/test/compile-fail/issue-24968.rs b/src/test/ui/resolve/issue-24968.rs index f51b77b0ee5..0d562cab6b8 100644 --- a/src/test/compile-fail/issue-24968.rs +++ b/src/test/ui/resolve/issue-24968.rs @@ -9,7 +9,8 @@ // except according to those terms. fn foo(_: Self) { - //~^ ERROR use of `Self` outside of an impl or trait +//~^ ERROR unresolved type `Self` +//~| NOTE `Self` is only available in traits and impls } fn main() {} diff --git a/src/test/ui/resolve/issue-24968.stderr b/src/test/ui/resolve/issue-24968.stderr new file mode 100644 index 00000000000..3050bc4eb62 --- /dev/null +++ b/src/test/ui/resolve/issue-24968.stderr @@ -0,0 +1,8 @@ +error[E0411]: unresolved type `Self` + --> $DIR/issue-24968.rs:11:11 + | +11 | fn foo(_: Self) { + | ^^^^ `Self` is only available in traits and impls + +error: aborting due to previous error + diff --git a/src/test/compile-fail/issue-33876.rs b/src/test/ui/resolve/issue-33876.rs index 87747d2851f..a97cc5e4101 100644 --- a/src/test/compile-fail/issue-33876.rs +++ b/src/test/ui/resolve/issue-33876.rs @@ -17,7 +17,6 @@ trait Bar {} impl Bar for Foo {} fn main() { - let any: &Any = &Bar; //~ ERROR E0425 - //~| HELP trait `Bar` + let any: &Any = &Bar; //~ ERROR expected value, found trait `Bar` if any.is::<u32>() { println!("u32"); } } diff --git a/src/test/ui/resolve/issue-33876.stderr b/src/test/ui/resolve/issue-33876.stderr new file mode 100644 index 00000000000..5dbecc4f0c5 --- /dev/null +++ b/src/test/ui/resolve/issue-33876.stderr @@ -0,0 +1,8 @@ +error[E0423]: expected value, found trait `Bar` + --> $DIR/issue-33876.rs:20:22 + | +20 | let any: &Any = &Bar; //~ ERROR expected value, found trait `Bar` + | ^^^ not a value + +error: aborting due to previous error + diff --git a/src/test/compile-fail/issue-3907-2.rs b/src/test/ui/resolve/issue-3907-2.rs index 130647966f2..130647966f2 100644 --- a/src/test/compile-fail/issue-3907-2.rs +++ b/src/test/ui/resolve/issue-3907-2.rs diff --git a/src/test/ui/resolve/issue-3907-2.stderr b/src/test/ui/resolve/issue-3907-2.stderr new file mode 100644 index 00000000000..ef02250e21c --- /dev/null +++ b/src/test/ui/resolve/issue-3907-2.stderr @@ -0,0 +1,10 @@ +error[E0038]: the trait `issue_3907::Foo` cannot be made into an object + --> $DIR/issue-3907-2.rs:20:1 + | +20 | fn bar(_x: Foo) {} + | ^^^^^^^^^^^^^^^^^^ the trait `issue_3907::Foo` cannot be made into an object + | + = note: method `bar` has no receiver + +error: aborting due to previous error + diff --git a/src/test/compile-fail/issue-3907.rs b/src/test/ui/resolve/issue-3907.rs index 86906ed9af2..0e54e68df0d 100644 --- a/src/test/compile-fail/issue-3907.rs +++ b/src/test/ui/resolve/issue-3907.rs @@ -17,9 +17,7 @@ struct S { name: isize } -impl Foo for S { //~ ERROR: `Foo` is not a trait - //~| NOTE: expected trait, found type alias - //~| NOTE: type aliases cannot be used for traits +impl Foo for S { //~ ERROR expected trait, found type alias `Foo` fn bar() { } } diff --git a/src/test/ui/resolve/issue-3907.stderr b/src/test/ui/resolve/issue-3907.stderr new file mode 100644 index 00000000000..0a402680aa8 --- /dev/null +++ b/src/test/ui/resolve/issue-3907.stderr @@ -0,0 +1,11 @@ +error[E0404]: expected trait, found type alias `Foo` + --> $DIR/issue-3907.rs:20:6 + | +20 | impl Foo for S { //~ ERROR expected trait, found type alias `Foo` + | ^^^ type aliases cannot be used for traits + | + = help: possible better candidate is found in another module, you can import it into scope: + = help: `use issue_3907::Foo;` + +error: cannot continue compilation due to previous error + diff --git a/src/test/compile-fail/issue-5035-2.rs b/src/test/ui/resolve/issue-5035-2.rs index 83ff95cc2ea..83ff95cc2ea 100644 --- a/src/test/compile-fail/issue-5035-2.rs +++ b/src/test/ui/resolve/issue-5035-2.rs diff --git a/src/test/ui/resolve/issue-5035-2.stderr b/src/test/ui/resolve/issue-5035-2.stderr new file mode 100644 index 00000000000..72b1578e0d0 --- /dev/null +++ b/src/test/ui/resolve/issue-5035-2.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `I + 'static: std::marker::Sized` is not satisfied + --> $DIR/issue-5035-2.rs:14:8 + | +14 | fn foo(_x: K) {} //~ ERROR: `I + 'static: std::marker::Sized` is not satisfied + | ^^ the trait `std::marker::Sized` is not implemented for `I + 'static` + | + = note: `I + 'static` does not have a constant size known at compile-time + = note: all local variables must have a statically known size + +error: aborting due to previous error + diff --git a/src/test/compile-fail/issue-5035.rs b/src/test/ui/resolve/issue-5035.rs index 8ebcba47134..6263e6f6db4 100644 --- a/src/test/compile-fail/issue-5035.rs +++ b/src/test/ui/resolve/issue-5035.rs @@ -10,9 +10,8 @@ trait I {} type K = I; -impl K for isize {} //~ ERROR: `K` is not a trait - //~| NOTE: expected trait, found type alias - //~| NOTE: aliases cannot be used for traits +impl K for isize {} //~ ERROR expected trait, found type alias `K` + //~| NOTE type aliases cannot be used for traits use ImportError; //~ ERROR unresolved import `ImportError` [E0432] //~^ no `ImportError` in the root diff --git a/src/test/ui/resolve/issue-5035.stderr b/src/test/ui/resolve/issue-5035.stderr new file mode 100644 index 00000000000..6cb9a289379 --- /dev/null +++ b/src/test/ui/resolve/issue-5035.stderr @@ -0,0 +1,14 @@ +error[E0432]: unresolved import `ImportError` + --> $DIR/issue-5035.rs:16:5 + | +16 | use ImportError; //~ ERROR unresolved import `ImportError` [E0432] + | ^^^^^^^^^^^ no `ImportError` in the root + +error[E0404]: expected trait, found type alias `K` + --> $DIR/issue-5035.rs:13:6 + | +13 | impl K for isize {} //~ ERROR expected trait, found type alias `K` + | ^ type aliases cannot be used for traits + +error: cannot continue compilation due to previous error + diff --git a/src/test/compile-fail/issue-6702.rs b/src/test/ui/resolve/issue-6702.rs index 66ed817ffa8..b391ddf3469 100644 --- a/src/test/compile-fail/issue-6702.rs +++ b/src/test/ui/resolve/issue-6702.rs @@ -14,6 +14,6 @@ struct Monster { fn main() { - let _m = Monster(); //~ ERROR `Monster` is the name of a struct or - //~^ HELP did you mean to write: `Monster { /* fields */ }`? + let _m = Monster(); //~ ERROR expected function, found struct `Monster` + //~^ NOTE did you mean `Monster { /* fields */ }`? } diff --git a/src/test/ui/resolve/issue-6702.stderr b/src/test/ui/resolve/issue-6702.stderr new file mode 100644 index 00000000000..b50295752f2 --- /dev/null +++ b/src/test/ui/resolve/issue-6702.stderr @@ -0,0 +1,8 @@ +error[E0423]: expected function, found struct `Monster` + --> $DIR/issue-6702.rs:17:14 + | +17 | let _m = Monster(); //~ ERROR expected function, found struct `Monster` + | ^^^^^^^ did you mean `Monster { /* fields */ }`? + +error: aborting due to previous error + diff --git a/src/test/ui/resolve/resolve-assoc-suggestions.rs b/src/test/ui/resolve/resolve-assoc-suggestions.rs new file mode 100644 index 00000000000..53e26ddafec --- /dev/null +++ b/src/test/ui/resolve/resolve-assoc-suggestions.rs @@ -0,0 +1,58 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Make sure associated items are recommended only in appropriate contexts. + +struct S { + field: u8, +} + +trait Tr { + fn method(&self); + type Type; +} + +impl Tr for S { + type Type = u8; + + fn method(&self) { + let _: field; + //~^ ERROR unresolved type `field` + //~| NOTE no resolution found + let field(..); + //~^ ERROR unresolved tuple struct/variant `field` + //~| NOTE no resolution found + field; + //~^ ERROR unresolved value `field` + //~| NOTE did you mean `self.field`? + + let _: Type; + //~^ ERROR unresolved type `Type` + //~| NOTE did you mean `Self::Type`? + let Type(..); + //~^ ERROR unresolved tuple struct/variant `Type` + //~| NOTE no resolution found + Type; + //~^ ERROR unresolved value `Type` + //~| NOTE no resolution found + + let _: method; + //~^ ERROR unresolved type `method` + //~| NOTE no resolution found + let method(..); + //~^ ERROR unresolved tuple struct/variant `method` + //~| NOTE no resolution found + method; + //~^ ERROR unresolved value `method` + //~| NOTE did you mean `self.method(...)`? + } +} + +fn main() {} diff --git a/src/test/ui/resolve/resolve-assoc-suggestions.stderr b/src/test/ui/resolve/resolve-assoc-suggestions.stderr new file mode 100644 index 00000000000..98ead713947 --- /dev/null +++ b/src/test/ui/resolve/resolve-assoc-suggestions.stderr @@ -0,0 +1,56 @@ +error[E0412]: unresolved type `field` + --> $DIR/resolve-assoc-suggestions.rs:26:16 + | +26 | let _: field; + | ^^^^^ no resolution found + +error[E0531]: unresolved tuple struct/variant `field` + --> $DIR/resolve-assoc-suggestions.rs:29:13 + | +29 | let field(..); + | ^^^^^ no resolution found + +error[E0425]: unresolved value `field` + --> $DIR/resolve-assoc-suggestions.rs:32:9 + | +32 | field; + | ^^^^^ did you mean `self.field`? + +error[E0412]: unresolved type `Type` + --> $DIR/resolve-assoc-suggestions.rs:36:16 + | +36 | let _: Type; + | ^^^^ did you mean `Self::Type`? + +error[E0531]: unresolved tuple struct/variant `Type` + --> $DIR/resolve-assoc-suggestions.rs:39:13 + | +39 | let Type(..); + | ^^^^ no resolution found + +error[E0425]: unresolved value `Type` + --> $DIR/resolve-assoc-suggestions.rs:42:9 + | +42 | Type; + | ^^^^ no resolution found + +error[E0412]: unresolved type `method` + --> $DIR/resolve-assoc-suggestions.rs:46:16 + | +46 | let _: method; + | ^^^^^^ no resolution found + +error[E0531]: unresolved tuple struct/variant `method` + --> $DIR/resolve-assoc-suggestions.rs:49:13 + | +49 | let method(..); + | ^^^^^^ no resolution found + +error[E0425]: unresolved value `method` + --> $DIR/resolve-assoc-suggestions.rs:52:9 + | +52 | method; + | ^^^^^^ did you mean `self.method(...)`? + +error: aborting due to 9 previous errors + diff --git a/src/test/compile-fail/resolve-hint-macro.rs b/src/test/ui/resolve/resolve-hint-macro.rs index edaab012757..72fd9a79376 100644 --- a/src/test/compile-fail/resolve-hint-macro.rs +++ b/src/test/ui/resolve/resolve-hint-macro.rs @@ -10,6 +10,6 @@ fn main() { assert(true); - //~^ ERROR unresolved name `assert` - //~| NOTE did you mean the macro `assert!`? + //~^ ERROR expected function, found macro `assert` + //~| NOTE did you mean `assert!(...)`? } diff --git a/src/test/ui/resolve/resolve-hint-macro.stderr b/src/test/ui/resolve/resolve-hint-macro.stderr new file mode 100644 index 00000000000..ffb3f848430 --- /dev/null +++ b/src/test/ui/resolve/resolve-hint-macro.stderr @@ -0,0 +1,8 @@ +error[E0423]: expected function, found macro `assert` + --> $DIR/resolve-hint-macro.rs:12:5 + | +12 | assert(true); + | ^^^^^^ did you mean `assert!(...)`? + +error: aborting due to previous error + diff --git a/src/test/ui/resolve/resolve-speculative-adjustment.rs b/src/test/ui/resolve/resolve-speculative-adjustment.rs new file mode 100644 index 00000000000..95289e23f9e --- /dev/null +++ b/src/test/ui/resolve/resolve-speculative-adjustment.rs @@ -0,0 +1,44 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Make sure speculative path resolution works properly when resolution +// adjustment happens and no extra errors is reported. + +struct S { + field: u8, +} + +trait Tr { + fn method(&self); +} + +impl Tr for S { + fn method(&self) { + fn g() { + // Speculative resolution of `Self` and `self` silently fails, + // "did you mean" messages are not printed. + field; + //~^ ERROR unresolved value `field` + //~| NOTE no resolution found + method(); + //~^ ERROR unresolved function `method` + //~| NOTE no resolution found + } + + field; + //~^ ERROR unresolved value `field` + //~| NOTE did you mean `self.field`? + method(); + //~^ ERROR unresolved function `method` + //~| NOTE did you mean `self.method(...)`? + } +} + +fn main() {} diff --git a/src/test/ui/resolve/resolve-speculative-adjustment.stderr b/src/test/ui/resolve/resolve-speculative-adjustment.stderr new file mode 100644 index 00000000000..173b6905541 --- /dev/null +++ b/src/test/ui/resolve/resolve-speculative-adjustment.stderr @@ -0,0 +1,26 @@ +error[E0425]: unresolved value `field` + --> $DIR/resolve-speculative-adjustment.rs:27:13 + | +27 | field; + | ^^^^^ no resolution found + +error[E0425]: unresolved function `method` + --> $DIR/resolve-speculative-adjustment.rs:30:13 + | +30 | method(); + | ^^^^^^ no resolution found + +error[E0425]: unresolved value `field` + --> $DIR/resolve-speculative-adjustment.rs:35:9 + | +35 | field; + | ^^^^^ did you mean `self.field`? + +error[E0425]: unresolved function `method` + --> $DIR/resolve-speculative-adjustment.rs:38:9 + | +38 | method(); + | ^^^^^^ did you mean `self.method(...)`? + +error: aborting due to 4 previous errors + diff --git a/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs index 4a816ea7572..789bdfb414d 100644 --- a/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs +++ b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.rs @@ -25,52 +25,52 @@ pub mod a { fn h1() -> i32 { a.I - //~^ ERROR E0425 - //~| HELP to reference an item from the `a` module, use `a::I` + //~^ ERROR expected value, found module `a` + //~| NOTE did you mean `a::I`? } fn h2() -> i32 { a.g() - //~^ ERROR E0425 - //~| HELP to call a function from the `a` module, use `a::g(..)` + //~^ ERROR expected value, found module `a` + //~| NOTE did you mean `a::g(...)`? } fn h3() -> i32 { a.b.J - //~^ ERROR E0425 - //~| HELP to reference an item from the `a` module, use `a::b` + //~^ ERROR expected value, found module `a` + //~| NOTE did you mean `a::b`? } fn h4() -> i32 { a::b.J - //~^ ERROR E0425 - //~| HELP to reference an item from the `a::b` module, use `a::b::J` + //~^ ERROR expected value, found module `a::b` + //~| NOTE did you mean `a::b::J`? } fn h5() { a.b.f(); - //~^ ERROR E0425 - //~| HELP to reference an item from the `a` module, use `a::b` + //~^ ERROR expected value, found module `a` + //~| NOTE did you mean `a::b`? let v = Vec::new(); v.push(a::b); - //~^ ERROR E0425 - //~| HELP module `a::b` cannot be used as an expression + //~^ ERROR expected value, found module `a::b` + //~| NOTE not a value } fn h6() -> i32 { a::b.f() - //~^ ERROR E0425 - //~| HELP to call a function from the `a::b` module, use `a::b::f(..)` + //~^ ERROR expected value, found module `a::b` + //~| NOTE did you mean `a::b::f(...)`? } fn h7() { a::b - //~^ ERROR E0425 - //~| HELP module `a::b` cannot be used as an expression + //~^ ERROR expected value, found module `a::b` + //~| NOTE not a value } fn h8() -> i32 { a::b() - //~^ ERROR E0425 - //~| HELP module `a::b` cannot be used as an expression + //~^ ERROR expected function, found module `a::b` + //~| NOTE not a function } diff --git a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr new file mode 100644 index 00000000000..8ace738ad6d --- /dev/null +++ b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr @@ -0,0 +1,58 @@ +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:27:5 + | +27 | a.I + | ^ did you mean `a::I`? + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:33:5 + | +33 | a.g() + | ^ did you mean `a::g(...)`? + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:39:5 + | +39 | a.b.J + | ^ did you mean `a::b`? + +error[E0423]: expected value, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:45:5 + | +45 | a::b.J + | ^^^^ did you mean `a::b::J`? + +error[E0423]: expected value, found module `a` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:51:5 + | +51 | a.b.f(); + | ^ did you mean `a::b`? + +error[E0423]: expected value, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:55:12 + | +55 | v.push(a::b); + | ^^^^ not a value + +error[E0423]: expected value, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:61:5 + | +61 | a::b.f() + | ^^^^ did you mean `a::b::f(...)`? + +error[E0423]: expected value, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:67:5 + | +67 | a::b + | ^^^^ not a value + +error[E0423]: expected function, found module `a::b` + --> $DIR/suggest-path-instead-of-mod-dot-item.rs:73:5 + | +73 | a::b() + | ^^^^ not a function + +error: main function not found + +error: aborting due to 10 previous errors + diff --git a/src/test/compile-fail/token-error-correct-2.rs b/src/test/ui/resolve/token-error-correct-2.rs index 151c1d432ed..6fa1260d180 100644 --- a/src/test/compile-fail/token-error-correct-2.rs +++ b/src/test/ui/resolve/token-error-correct-2.rs @@ -11,8 +11,9 @@ // Test that we do some basic error correcton in the tokeniser (and don't ICE). fn main() { - if foo { //~ NOTE: unclosed delimiter - //~^ ERROR: unresolved name `foo` - //~| NOTE unresolved name + if foo { + //~^ NOTE: unclosed delimiter + //~| ERROR: unresolved value `foo` + //~| NOTE: no resolution found ) //~ ERROR: incorrect close delimiter: `)` } diff --git a/src/test/ui/resolve/token-error-correct-2.stderr b/src/test/ui/resolve/token-error-correct-2.stderr new file mode 100644 index 00000000000..7c422398e21 --- /dev/null +++ b/src/test/ui/resolve/token-error-correct-2.stderr @@ -0,0 +1,20 @@ +error: incorrect close delimiter: `)` + --> $DIR/token-error-correct-2.rs:18:5 + | +18 | ) //~ ERROR: incorrect close delimiter: `)` + | ^ + | +note: unclosed delimiter + --> $DIR/token-error-correct-2.rs:14:12 + | +14 | if foo { + | ^ + +error[E0425]: unresolved value `foo` + --> $DIR/token-error-correct-2.rs:14:8 + | +14 | if foo { + | ^^^ no resolution found + +error: aborting due to 2 previous errors + diff --git a/src/test/compile-fail/token-error-correct-3.rs b/src/test/ui/resolve/token-error-correct-3.rs index 5f21bf18d7b..f72b7adf593 100644 --- a/src/test/compile-fail/token-error-correct-3.rs +++ b/src/test/ui/resolve/token-error-correct-3.rs @@ -18,8 +18,8 @@ pub mod raw { pub fn ensure_dir_exists<P: AsRef<Path>, F: FnOnce(&Path)>(path: P, callback: F) -> io::Result<bool> { - if !is_directory(path.as_ref()) { //~ ERROR: unresolved name `is_directory` - //~| NOTE unresolved name + if !is_directory(path.as_ref()) { //~ ERROR: unresolved function `is_directory` + //~^ NOTE: no resolution found callback(path.as_ref(); //~ NOTE: unclosed delimiter //~^ ERROR: expected one of fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types diff --git a/src/test/ui/resolve/token-error-correct-3.stderr b/src/test/ui/resolve/token-error-correct-3.stderr new file mode 100644 index 00000000000..0b15c23909c --- /dev/null +++ b/src/test/ui/resolve/token-error-correct-3.stderr @@ -0,0 +1,45 @@ +error: incorrect close delimiter: `}` + --> $DIR/token-error-correct-3.rs:29:9 + | +29 | } else { //~ ERROR: incorrect close delimiter: `}` + | ^ + | +note: unclosed delimiter + --> $DIR/token-error-correct-3.rs:23:21 + | +23 | callback(path.as_ref(); //~ NOTE: unclosed delimiter + | ^ + +error: expected one of `,`, `.`, `?`, or an operator, found `;` + --> $DIR/token-error-correct-3.rs:23:35 + | +23 | callback(path.as_ref(); //~ NOTE: unclosed delimiter + | ^ + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `)` + --> $DIR/token-error-correct-3.rs:29:9 + | +29 | } else { //~ ERROR: incorrect close delimiter: `}` + | ^ + +error[E0425]: unresolved function `is_directory` + --> $DIR/token-error-correct-3.rs:21:13 + | +21 | if !is_directory(path.as_ref()) { //~ ERROR: unresolved function `is_directory` + | ^^^^^^^^^^^^ no resolution found + +error[E0308]: mismatched types + --> $DIR/token-error-correct-3.rs:25:13 + | +25 | fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result` + | + = note: expected type `()` + = note: found type `std::result::Result<bool, std::io::Error>` + = help: here are some functions which might fulfill your needs: + - .unwrap() + - .unwrap_err() + - .unwrap_or_default() + +error: aborting due to previous error + diff --git a/src/test/compile-fail/token-error-correct.rs b/src/test/ui/resolve/token-error-correct.rs index 3ba9edda07f..5fd35e51336 100644 --- a/src/test/compile-fail/token-error-correct.rs +++ b/src/test/ui/resolve/token-error-correct.rs @@ -11,14 +11,16 @@ // Test that we do some basic error correcton in the tokeniser. fn main() { - foo(bar(; //~ NOTE: unclosed delimiter + foo(bar(; //~^ NOTE: unclosed delimiter - //~^^ ERROR: expected expression, found `;` - //~^^^ ERROR: unresolved name `bar` - //~^^^^ ERROR: unresolved name `foo` - //~^^^^^ ERROR: expected one of `)`, `,`, `.`, `<`, `?` - //~| NOTE unresolved name - //~| NOTE unresolved name -} //~ ERROR: incorrect close delimiter: `}` + //~| NOTE: unclosed delimiter + //~| ERROR: expected expression, found `;` + //~| ERROR: unresolved function `foo` + //~| NOTE: no resolution found + //~| ERROR: unresolved function `bar` + //~| NOTE: no resolution found + //~| ERROR: expected one of `)`, `,`, `.`, `<`, `?` +} //~^ ERROR: incorrect close delimiter: `}` -//~^^ ERROR: expected expression, found `)` +//~| ERROR: incorrect close delimiter: `}` +//~| ERROR: expected expression, found `)` diff --git a/src/test/ui/resolve/token-error-correct.stderr b/src/test/ui/resolve/token-error-correct.stderr new file mode 100644 index 00000000000..38cd95a4867 --- /dev/null +++ b/src/test/ui/resolve/token-error-correct.stderr @@ -0,0 +1,56 @@ +error: incorrect close delimiter: `}` + --> $DIR/token-error-correct.rs:23:1 + | +23 | } + | ^ + | +note: unclosed delimiter + --> $DIR/token-error-correct.rs:14:12 + | +14 | foo(bar(; + | ^ + +error: incorrect close delimiter: `}` + --> $DIR/token-error-correct.rs:23:1 + | +23 | } + | ^ + | +note: unclosed delimiter + --> $DIR/token-error-correct.rs:14:8 + | +14 | foo(bar(; + | ^ + +error: expected expression, found `;` + --> $DIR/token-error-correct.rs:14:13 + | +14 | foo(bar(; + | ^ + +error: expected one of `)`, `,`, `.`, `<`, `?`, `break`, `continue`, `false`, `for`, `if`, `loop`, `match`, `move`, `return`, `true`, `unsafe`, `while`, or an operator, found `;` + --> $DIR/token-error-correct.rs:14:13 + | +14 | foo(bar(; + | ^ + +error: expected expression, found `)` + --> $DIR/token-error-correct.rs:23:1 + | +23 | } + | ^ + +error[E0425]: unresolved function `foo` + --> $DIR/token-error-correct.rs:14:5 + | +14 | foo(bar(; + | ^^^ no resolution found + +error[E0425]: unresolved function `bar` + --> $DIR/token-error-correct.rs:14:9 + | +14 | foo(bar(; + | ^^^ no resolution found + +error: aborting due to 7 previous errors + diff --git a/src/test/ui/resolve/tuple-struct-alias.rs b/src/test/ui/resolve/tuple-struct-alias.rs new file mode 100644 index 00000000000..c9c05202fea --- /dev/null +++ b/src/test/ui/resolve/tuple-struct-alias.rs @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct S(u8, u16); +type A = S; + +impl S { + fn f() { + let s = Self(0, 1); + match s { + Self(..) => {} + } + } +} + +fn main() { + let s = A(0, 1); + match s { + A(..) => {} + } +} diff --git a/src/test/ui/resolve/tuple-struct-alias.stderr b/src/test/ui/resolve/tuple-struct-alias.stderr new file mode 100644 index 00000000000..485c8ebbaeb --- /dev/null +++ b/src/test/ui/resolve/tuple-struct-alias.stderr @@ -0,0 +1,26 @@ +error[E0423]: expected function, found self type `Self` + --> $DIR/tuple-struct-alias.rs:16:17 + | +16 | let s = Self(0, 1); + | ^^^^ did you mean `Self { /* fields */ }`? + +error[E0532]: expected tuple struct/variant, found self type `Self` + --> $DIR/tuple-struct-alias.rs:18:13 + | +18 | Self(..) => {} + | ^^^^ did you mean `Self { /* fields */ }`? + +error[E0423]: expected function, found type alias `A` + --> $DIR/tuple-struct-alias.rs:24:13 + | +24 | let s = A(0, 1); + | ^ did you mean `A { /* fields */ }`? + +error[E0532]: expected tuple struct/variant, found type alias `A` + --> $DIR/tuple-struct-alias.rs:26:9 + | +26 | A(..) => {} + | ^ did you mean `A { /* fields */ }`? + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.rs b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.rs new file mode 100644 index 00000000000..57f6ddd2d3c --- /dev/null +++ b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.rs @@ -0,0 +1,21 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn f<F:Nonexist(isize) -> isize>(x: F) {} +//~^ ERROR unresolved trait `Nonexist` +//~| NOTE no resolution found + +type Typedef = isize; + +fn g<F:Typedef(isize) -> isize>(x: F) {} +//~^ ERROR expected trait, found type alias `Typedef` +//~| NOTE type aliases cannot be used for traits + +fn main() {} diff --git a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr new file mode 100644 index 00000000000..ba90321ca2b --- /dev/null +++ b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr @@ -0,0 +1,14 @@ +error[E0405]: unresolved trait `Nonexist` + --> $DIR/unboxed-closure-sugar-nonexistent-trait.rs:11:8 + | +11 | fn f<F:Nonexist(isize) -> isize>(x: F) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ no resolution found + +error[E0404]: expected trait, found type alias `Typedef` + --> $DIR/unboxed-closure-sugar-nonexistent-trait.rs:17:8 + | +17 | fn g<F:Typedef(isize) -> isize>(x: F) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ type aliases cannot be used for traits + +error: cannot continue compilation due to previous error + diff --git a/src/test/ui/resolve/unresolved_static_type_field.rs b/src/test/ui/resolve/unresolved_static_type_field.rs new file mode 100644 index 00000000000..19beabd8823 --- /dev/null +++ b/src/test/ui/resolve/unresolved_static_type_field.rs @@ -0,0 +1,27 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn f(_: bool) {} + +struct Foo { + cx: bool, +} + +impl Foo { + fn bar() { + f(cx); + //~^ ERROR unresolved value `cx` + //~| ERROR unresolved value `cx` + //~| NOTE did you mean `self.cx`? + //~| NOTE `self` value is only available in methods with `self` parameter + } +} + +fn main() {} diff --git a/src/test/ui/resolve/unresolved_static_type_field.stderr b/src/test/ui/resolve/unresolved_static_type_field.stderr new file mode 100644 index 00000000000..1d0ee191175 --- /dev/null +++ b/src/test/ui/resolve/unresolved_static_type_field.stderr @@ -0,0 +1,11 @@ +error[E0425]: unresolved value `cx` + --> $DIR/unresolved_static_type_field.rs:19:11 + | +19 | f(cx); + | ^^ + | | + | did you mean `self.cx`? + | `self` value is only available in methods with `self` parameter + +error: aborting due to previous error + diff --git a/src/test/ui/span/coerce-suggestions.rs b/src/test/ui/span/coerce-suggestions.rs new file mode 100644 index 00000000000..3177e858ff4 --- /dev/null +++ b/src/test/ui/span/coerce-suggestions.rs @@ -0,0 +1,47 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax)] + +fn test(_x: &mut String) {} +fn test2(_x: &mut i32) {} + +fn main() { + let x: usize = String::new(); + //~^ ERROR E0308 + //~| NOTE expected usize, found struct `std::string::String` + //~| NOTE expected type `usize` + //~| NOTE found type `std::string::String` + //~| HELP here are some functions which might fulfill your needs: + let x: &str = String::new(); + //~^ ERROR E0308 + //~| NOTE expected &str, found struct `std::string::String` + //~| NOTE expected type `&str` + //~| NOTE found type `std::string::String` + //~| HELP try with `&String::new()` + let y = String::new(); + test(&y); + //~^ ERROR E0308 + //~| NOTE types differ in mutability + //~| NOTE expected type `&mut std::string::String` + //~| NOTE found type `&std::string::String` + //~| HELP try with `&mut y` + test2(&y); + //~^ ERROR E0308 + //~| NOTE types differ in mutability + //~| NOTE expected type `&mut i32` + //~| NOTE found type `&std::string::String` + let f; + f = box f; + //~^ ERROR E0308 + //~| NOTE cyclic type of infinite size + //~| NOTE expected type `_` + //~| NOTE found type `Box<_>` +} diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr new file mode 100644 index 00000000000..e3164661507 --- /dev/null +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -0,0 +1,55 @@ +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:17:20 + | +17 | let x: usize = String::new(); + | ^^^^^^^^^^^^^ expected usize, found struct `std::string::String` + | + = note: expected type `usize` + = note: found type `std::string::String` + = help: here are some functions which might fulfill your needs: + - .capacity() + - .len() + +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:23:19 + | +23 | let x: &str = String::new(); + | ^^^^^^^^^^^^^ expected &str, found struct `std::string::String` + | + = note: expected type `&str` + = note: found type `std::string::String` + = help: here are some functions which might fulfill your needs: + - .as_str() + - .trim() + - .trim_left() + - .trim_right() + +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:30:10 + | +30 | test(&y); + | ^^ types differ in mutability + | + = note: expected type `&mut std::string::String` + = note: found type `&std::string::String` + +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:36:11 + | +36 | test2(&y); + | ^^ types differ in mutability + | + = note: expected type `&mut i32` + = note: found type `&std::string::String` + +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:42:9 + | +42 | f = box f; + | ^^^^^ cyclic type of infinite size + | + = note: expected type `_` + = note: found type `Box<_>` + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/span/impl-wrong-item-for-trait.stderr b/src/test/ui/span/impl-wrong-item-for-trait.stderr index 815893e0c82..717f5ee200c 100644 --- a/src/test/ui/span/impl-wrong-item-for-trait.stderr +++ b/src/test/ui/span/impl-wrong-item-for-trait.stderr @@ -1,3 +1,9 @@ +error[E0437]: type `bar` is not a member of trait `Foo` + --> $DIR/impl-wrong-item-for-trait.rs:47:5 + | +47 | type bar = u64; + | ^^^^^^^^^^^^^^^ not a member of trait `Foo` + error[E0323]: item `bar` is an associated const, which doesn't match its trait `Foo` --> $DIR/impl-wrong-item-for-trait.rs:25:5 | diff --git a/src/test/ui/span/issue-35987.stderr b/src/test/ui/span/issue-35987.stderr index 2370b3d6c61..764f34cabde 100644 --- a/src/test/ui/span/issue-35987.stderr +++ b/src/test/ui/span/issue-35987.stderr @@ -1,10 +1,11 @@ -error[E0404]: `Add` is not a trait +error[E0404]: expected trait, found type parameter `Add` --> $DIR/issue-35987.rs:15:21 | 15 | impl<T: Clone, Add> Add for Foo<T> { - | --- ^^^ expected trait, found type parameter - | | - | type parameter defined here + | ^^^ not a trait + | + = help: possible better candidate is found in another module, you can import it into scope: + = help: `use std::ops::Add;` error: main function not found diff --git a/src/test/ui/span/typo-suggestion.stderr b/src/test/ui/span/typo-suggestion.stderr index 5446175aa25..117c38e1db9 100644 --- a/src/test/ui/span/typo-suggestion.stderr +++ b/src/test/ui/span/typo-suggestion.stderr @@ -1,10 +1,10 @@ -error[E0425]: unresolved name `bar` +error[E0425]: unresolved value `bar` --> $DIR/typo-suggestion.rs:15:26 | 15 | println!("Hello {}", bar); - | ^^^ unresolved name + | ^^^ no resolution found -error[E0425]: unresolved name `fob` +error[E0425]: unresolved value `fob` --> $DIR/typo-suggestion.rs:18:26 | 18 | println!("Hello {}", fob); diff --git a/src/tools/cargotest/main.rs b/src/tools/cargotest/main.rs index 800186a926d..26a2e96f571 100644 --- a/src/tools/cargotest/main.rs +++ b/src/tools/cargotest/main.rs @@ -24,7 +24,7 @@ struct Test { const TEST_REPOS: &'static [Test] = &[Test { name: "cargo", repo: "https://github.com/rust-lang/cargo", - sha: "806e3c368a15f618244a3b4e918bf77f9c403fd0", + sha: "b7be4f2ef2cf743492edc6dfb55d087ed88f2d76", lock: None, }, Test { diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 34f3837d8bb..1aeb76c0a0e 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -127,6 +127,9 @@ pub struct Config { // Only run tests that match this filter pub filter: Option<String>, + // Exactly match the filter, rather than a substring + pub filter_exact: bool, + // Write out a parseable log of tests that were run pub logfile: Option<PathBuf>, diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index d8681c9d6ed..e4d9836c562 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -89,6 +89,7 @@ pub fn parse_config(args: Vec<String> ) -> Config { "(compile-fail|parse-fail|run-fail|run-pass|\ run-pass-valgrind|pretty|debug-info|incremental|mir-opt)"), optflag("", "ignored", "run tests marked as ignored"), + optflag("", "exact", "filters match exactly"), optopt("", "runtool", "supervisor program to run tests under \ (eg. emulator, valgrind)", "PROGRAM"), optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"), @@ -167,6 +168,7 @@ pub fn parse_config(args: Vec<String> ) -> Config { mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"), run_ignored: matches.opt_present("ignored"), filter: matches.free.first().cloned(), + filter_exact: matches.opt_present("exact"), logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)), runtool: matches.opt_str("runtool"), host_rustcflags: matches.opt_str("host-rustcflags"), @@ -216,6 +218,7 @@ pub fn log_config(config: &Config) { opt_str(&config.filter .as_ref() .map(|re| re.to_owned())))); + logv(c, format!("filter_exact: {}", config.filter_exact)); logv(c, format!("runtool: {}", opt_str(&config.runtool))); logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags))); @@ -309,6 +312,7 @@ pub fn run_tests(config: &Config) { pub fn test_opts(config: &Config) -> test::TestOpts { test::TestOpts { filter: config.filter.clone(), + filter_exact: config.filter_exact, run_ignored: config.run_ignored, quiet: config.quiet, logfile: config.logfile.clone(), @@ -321,6 +325,7 @@ pub fn test_opts(config: &Config) -> test::TestOpts { color: test::AutoColor, test_threads: None, skip: vec![], + list: false, } } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 3cc14541fcd..d729deb8605 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2108,7 +2108,16 @@ actual:\n\ } self.create_dir_racy(&tmpdir); - let mut cmd = Command::new("make"); + let host = &self.config.host; + let make = if host.contains("bitrig") || host.contains("dragonfly") || + host.contains("freebsd") || host.contains("netbsd") || + host.contains("openbsd") { + "gmake" + } else { + "make" + }; + + let mut cmd = Command::new(make); cmd.current_dir(&self.testpaths.file) .env("TARGET", &self.config.target) .env("PYTHON", &self.config.docck_python) @@ -2294,7 +2303,18 @@ actual:\n\ }; } if !found { - panic!("ran out of mir dump output to match against"); + let normalize_all = dumped_string.lines() + .map(nocomment_mir_line) + .filter(|l| !l.is_empty()) + .collect::<Vec<_>>() + .join("\n"); + panic!("ran out of mir dump output to match against.\n\ + Did not find expected line: {:?}\n\ + Expected:\n{}\n\ + Actual:\n{}", + expected_line, + expected_content.join("\n"), + normalize_all); } } } @@ -2439,11 +2459,14 @@ enum TargetLocation { } fn normalize_mir_line(line: &str) -> String { - let no_comments = if let Some(idx) = line.find("//") { + nocomment_mir_line(line).replace(char::is_whitespace, "") +} + +fn nocomment_mir_line(line: &str) -> &str { + if let Some(idx) = line.find("//") { let (l, _) = line.split_at(idx); - l + l.trim_right() } else { line - }; - no_comments.replace(char::is_whitespace, "") + } } diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 4ef07f7e4b8..ac5dff0980c 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -67,7 +67,7 @@ pub fn check(path: &Path, bad: &mut bool) { } contents.truncate(0); - t!(t!(File::open(file)).read_to_string(&mut contents)); + t!(t!(File::open(&file), &file).read_to_string(&mut contents)); for (i, line) in contents.lines().enumerate() { let mut err = |msg: &str| { diff --git a/src/vendor/gcc/.cargo-checksum.json b/src/vendor/gcc/.cargo-checksum.json index efe1ebb7d44..e85f4b21813 100644 --- a/src/vendor/gcc/.cargo-checksum.json +++ b/src/vendor/gcc/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805",".travis.yml":"5cee7774cf6d876246a0ae0f8362cceeecec5924b751049c945faac9342565ff","Cargo.toml":"2634dedd87889b33a794e31b41a8d8d4713ef40382be3d464229707679bd83da","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"ecb2d93f4c81edbd48d8742ff7887dc0a4530a5890967839090bbc972d49bebe","appveyor.yml":"46c77d913eaa45871296942c2cd96ef092c9dcaf19201cb5c500a5107faeb06f","src/bin/gcc-shim.rs":"11edfe1fc6f932bd42ffffda5145833302bc163e0b87dc0d54f4bd0997ad4708","src/lib.rs":"5eb0e311367226ed0420f5e2dac10cc35fc0a3be639a612b6e8ea6d24f646634","src/registry.rs":"3e2a42581ebb82e325dd5600c6571cef937b35003b2927dc618967f5238a2058","src/windows_registry.rs":"906653c020ffe9d572e435f3fc3a8892d9e0a13240ba297db01ce0a288e08cdb","tests/cc_env.rs":"d92c5e3d3d43ac244e63b2cd2c93a521fcf124bf1ccf8d4c6bfa7f8333d88976","tests/support/mod.rs":"d11ed0db4dda5ecf5fb970c9b0c56428cd47421a2742f07032e2cc6b0a0f07e2","tests/test.rs":"164220f11be2eebc20315826513999970660a82feff8cc4b15b4e9d73d98324e"},"package":"553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5"} \ No newline at end of file +{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805",".travis.yml":"675ffe583db77282d010306f29e6d81e5070ab081deddd0300137dfbd2cb83de","Cargo.toml":"19bb617b74de761515ef5d087fd0e30912fda1d7c22fd04fa211236dab99a509","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"ecb2d93f4c81edbd48d8742ff7887dc0a4530a5890967839090bbc972d49bebe","appveyor.yml":"46c77d913eaa45871296942c2cd96ef092c9dcaf19201cb5c500a5107faeb06f","src/bin/gcc-shim.rs":"11edfe1fc6f932bd42ffffda5145833302bc163e0b87dc0d54f4bd0997ad4708","src/lib.rs":"7e7c60beccfdd145e876da81bb07dd09c5248dab0b26d93190bab4242799d51a","src/registry.rs":"3e2a42581ebb82e325dd5600c6571cef937b35003b2927dc618967f5238a2058","src/windows_registry.rs":"1f4211caec5a192b5f05c8a47efb27aa6a0ab976c659b9318a0cf603a28d6746","tests/cc_env.rs":"d92c5e3d3d43ac244e63b2cd2c93a521fcf124bf1ccf8d4c6bfa7f8333d88976","tests/support/mod.rs":"f4dad5a8133c3dd6678d9a3de057b82e624ef547b9b3e4ac9508a48962fc387b","tests/test.rs":"164220f11be2eebc20315826513999970660a82feff8cc4b15b4e9d73d98324e"},"package":"872db9e59486ef2b14f8e8c10e9ef02de2bccef6363d7f34835dedb386b3d950"} \ No newline at end of file diff --git a/src/vendor/gcc/.travis.yml b/src/vendor/gcc/.travis.yml index 6b508b9d8bc..bf55f49173d 100644 --- a/src/vendor/gcc/.travis.yml +++ b/src/vendor/gcc/.travis.yml @@ -19,12 +19,14 @@ script: - cargo test --manifest-path gcc-test/Cargo.toml --target $TARGET --features parallel - cargo test --manifest-path gcc-test/Cargo.toml --target $TARGET --release - cargo doc + - cargo clean && cargo build - rustdoc --test README.md -L target/debug -L target/debug/deps after_success: - travis-cargo --only nightly doc-upload env: global: - secure: ilbcq9zX+UaiBcwqkBGldeanbEQus9npLsi0/nF1PUxKbQsoWSVtVOehAD8Hy92D3hX2npIRyNL8GxBn85XEcBYc1h7DiWUhLcXfZie79v8Ly/qboHCfZLXlB1ofbypbyQfouEdOE9zHf0ZILYVpAgUkliv6KuVShsrKNlbn4QE= + secure: "CBtqrudgE0PS8x3kTr44jKbC2D4nfnmdYVecooNm0qnER4B4TSvZpZSQoCgKK6k4BYQuOSyFTOwYx6M79w39ZMOgyCP9ytB+tyMWL0/+ZuUQL04yVg4M5vd3oJMkOaXbvG56ncgPyFrseY+FPDg+mXAzvJk/nily37YXjkQj2D0=" + matrix: - ARCH=x86_64 - ARCH=i686 diff --git a/src/vendor/gcc/Cargo.toml b/src/vendor/gcc/Cargo.toml index fd51ce0e9f4..7efdbf9b4b3 100644 --- a/src/vendor/gcc/Cargo.toml +++ b/src/vendor/gcc/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gcc" -version = "0.3.38" +version = "0.3.40" authors = ["Alex Crichton <alex@alexcrichton.com>"] license = "MIT/Apache-2.0" repository = "https://github.com/alexcrichton/gcc-rs" diff --git a/src/vendor/gcc/src/lib.rs b/src/vendor/gcc/src/lib.rs index f319e9313ad..43cc371117f 100644 --- a/src/vendor/gcc/src/lib.rs +++ b/src/vendor/gcc/src/lib.rs @@ -353,6 +353,23 @@ impl Config { self.compile_objects(&src_dst); self.assemble(lib_name, &dst.join(output), &objects); + if self.get_target().contains("msvc") { + let compiler = self.get_base_compiler(); + let atlmfc_lib = compiler.env().iter().find(|&&(ref var, _)| { + var == OsStr::new("LIB") + }).and_then(|&(_, ref lib_paths)| { + env::split_paths(lib_paths).find(|path| { + let sub = Path::new("atlmfc/lib"); + path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub)) + }) + }); + + if let Some(atlmfc_lib) = atlmfc_lib { + self.print(&format!("cargo:rustc-link-search=native={}", + atlmfc_lib.display())); + } + } + self.print(&format!("cargo:rustc-link-lib=static={}", &output[3..output.len() - 2])); self.print(&format!("cargo:rustc-link-search=native={}", dst.display())); @@ -446,17 +463,20 @@ impl Config { if msvc { cmd.args.push("/nologo".into()); - cmd.args.push("/MD".into()); // link against msvcrt.dll for now + let features = env::var("CARGO_CFG_TARGET_FEATURE") + .unwrap_or(String::new()); + if features.contains("crt-static") { + cmd.args.push("/MT".into()); + } else { + cmd.args.push("/MD".into()); + } match &opt_level[..] { "z" | "s" => cmd.args.push("/Os".into()), "2" => cmd.args.push("/O2".into()), "1" => cmd.args.push("/O1".into()), _ => {} } - if target.contains("i686") { - cmd.args.push("/SAFESEH".into()); - } else if target.contains("i586") { - cmd.args.push("/SAFESEH".into()); + if target.contains("i586") { cmd.args.push("/ARCH:IA32".into()); } } else if nvcc { @@ -489,27 +509,48 @@ impl Config { cmd.args.push("-Xcompiler".into()); cmd.args.push("\'-fPIC\'".into()); } + if target.contains("musl") { cmd.args.push("-static".into()); } + // armv7 targets get to use armv7 instructions if target.starts_with("armv7-unknown-linux-") { cmd.args.push("-march=armv7-a".into()); } + + // On android we can guarantee some extra float instructions + // (specified in the android spec online) if target.starts_with("armv7-linux-androideabi") { cmd.args.push("-march=armv7-a".into()); cmd.args.push("-mfpu=vfpv3-d16".into()); } + + // For us arm == armv6 by default if target.starts_with("arm-unknown-linux-") { cmd.args.push("-march=armv6".into()); cmd.args.push("-marm".into()); } + + // Turn codegen down on i586 to avoid some instructions. if target.starts_with("i586-unknown-linux-") { cmd.args.push("-march=pentium".into()); } + + // Set codegen level for i686 correctly if target.starts_with("i686-unknown-linux-") { cmd.args.push("-march=i686".into()); } + + // Looks like `musl-gcc` makes is hard for `-m32` to make its way + // all the way to the linker, so we need to actually instruct the + // linker that we're generating 32-bit executables as well. This'll + // typically only be used for build scripts which transitively use + // these flags that try to compile executables. + if target == "i686-unknown-linux-musl" { + cmd.args.push("-Wl,-melf_i386".into()); + } + if target.starts_with("thumb") { cmd.args.push("-mthumb".into()); @@ -518,10 +559,14 @@ impl Config { } } if target.starts_with("thumbv6m") { - cmd.args.push("-march=armv6-m".into()); + cmd.args.push("-march=armv6s-m".into()); } if target.starts_with("thumbv7em") { cmd.args.push("-march=armv7e-m".into()); + + if target.ends_with("eabihf") { + cmd.args.push("-mfpu=fpv4-sp-d16".into()) + } } if target.starts_with("thumbv7m") { cmd.args.push("-march=armv7-m".into()); @@ -739,6 +784,7 @@ impl Config { "powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"), "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"), "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"), + "sparc64-unknown-netbsd" => Some("sparc64--netbsd"), "thumbv6m-none-eabi" => Some("arm-none-eabi"), "thumbv7em-none-eabi" => Some("arm-none-eabi"), "thumbv7em-none-eabihf" => Some("arm-none-eabi"), @@ -805,6 +851,8 @@ impl Config { None } else if target.contains("darwin") { Some("c++".to_string()) + } else if target.contains("freebsd") { + Some("c++".to_string()) } else { Some("stdc++".to_string()) } diff --git a/src/vendor/gcc/src/windows_registry.rs b/src/vendor/gcc/src/windows_registry.rs index b2c719d27ff..e16a33f2464 100644 --- a/src/vendor/gcc/src/windows_registry.rs +++ b/src/vendor/gcc/src/windows_registry.rs @@ -211,6 +211,11 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> { let sub = otry!(vc_lib_subdir(target)); tool.libs.push(path.join("lib").join(sub)); tool.include.push(path.join("include")); + let atlmfc_path = path.join("atlmfc"); + if atlmfc_path.exists() { + tool.libs.push(atlmfc_path.join("lib").join(sub)); + tool.include.push(atlmfc_path.join("include")); + } Some(tool) }).next() } diff --git a/src/vendor/gcc/tests/support/mod.rs b/src/vendor/gcc/tests/support/mod.rs index b5703d2fd8b..5c40984eb6a 100644 --- a/src/vendor/gcc/tests/support/mod.rs +++ b/src/vendor/gcc/tests/support/mod.rs @@ -23,6 +23,9 @@ impl Test { pub fn new() -> Test { let mut gcc = PathBuf::from(env::current_exe().unwrap()); gcc.pop(); + if gcc.ends_with("deps") { + gcc.pop(); + } gcc.push(format!("gcc-shim{}", env::consts::EXE_SUFFIX)); Test { td: TempDir::new("gcc-test").unwrap(), |
