diff options
268 files changed, 4303 insertions, 4325 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock index 00cc530e632..8c2d8fc989b 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -288,12 +288,12 @@ dependencies = [ [[package]] name = "clippy" -version = "0.0.200" +version = "0.0.202" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "cargo_metadata 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "clippy-mini-macro-test 0.2.0", - "clippy_lints 0.0.200", + "clippy_lints 0.0.202", "compiletest_rs 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "derive-new 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -311,6 +311,27 @@ version = "0.2.0" [[package]] name = "clippy_lints" version = "0.0.200" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cargo_metadata 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clippy_lints" +version = "0.0.202" dependencies = [ "cargo_metadata 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -588,7 +609,7 @@ dependencies = [ [[package]] name = "ena" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1478,16 +1499,17 @@ dependencies = [ [[package]] name = "racer" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_errors 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_syntax 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1623,7 +1645,7 @@ version = "0.128.0" dependencies = [ "cargo 0.29.0", "cargo_metadata 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy_lints 0.0.200", + "clippy_lints 0.0.200 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "json 0.11.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1632,12 +1654,12 @@ dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "racer 2.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "racer 2.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rls-analysis 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "rls-blacklist 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rls-data 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rls-rustc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rls-rustc 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rls-vfs 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustfmt-nightly 0.7.0", @@ -1645,6 +1667,7 @@ dependencies = [ "serde_derive 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1678,7 +1701,7 @@ dependencies = [ [[package]] name = "rls-rustc" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1696,7 +1719,7 @@ name = "rls-vfs" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "racer 2.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "racer 2.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1748,7 +1771,7 @@ version = "128.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ena 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ena 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1955,7 +1978,7 @@ name = "rustc_data_structures" version = "0.0.0" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ena 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ena 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2738,14 +2761,6 @@ dependencies = [ [[package]] name = "toml" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "toml" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -2984,6 +2999,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum chrono 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ba5f60682a4c264e7f8d77b82e7788938a76befdf949d4a98026d19099c9d873" "checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" +"checksum clippy_lints 0.0.200 (registry+https://github.com/rust-lang/crates.io-index)" = "d2432663f6bdb90255dcf9df5ca504f99b575bb471281591138f62f9d31f863b" "checksum cmake 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5cf678ceebedde428000cb3a34465cf3606d1a48da17014948a916deac39da7c" "checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" "checksum commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007" @@ -3005,7 +3021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum elasticlunr-rs 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4511b63d69dd5d31e8e29aed2c132c413f87acea8035d0584801feaab9dd1f0f" -"checksum ena 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b449f3b18c89d2dbe40548d2ee4fa58ea0a08b761992da6ecb9788e4688834" +"checksum ena 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dc8393b3c7352f94092497f6b52019643e493b6b890eb417cdb7c46117e621" "checksum endian-type 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" "checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" "checksum env_logger 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "be27f8ea102a7182093a80d98f0b78623b580eda8791cbe8e2345fe6e57567a6" @@ -3098,7 +3114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a" -"checksum racer 2.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "40d44bc30fc8d403b665286b2c9a83466ddbf69297668fb02b785c3e58eb8e0d" +"checksum racer 2.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "e713729f45f12df5c5e182d39506766f76c09133fb661d3622e0ddf8078911c2" "checksum radix_trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "03d0d770481e8af620ca61d3d304bf014f965d7f78e923dc58545e6a545070a9" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" @@ -3114,7 +3130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rls-analysis 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da9794cd1f80f2cb888c00641a32f9855d0226c954ee31cef145784914c7142e" "checksum rls-blacklist 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e4a9cc2545ccb7e05b355bfe047b8039a6ec12270d5f3c996b766b340a50f7d2" "checksum rls-data 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd20763e1c60ae8945384c8a8fa4ac44f8afa7b0a817511f5e8927e5d24f988" -"checksum rls-rustc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "885f66b92757420572cbb02e033d4a9558c7413ca9b7ac206f28fd58ffdb44ea" +"checksum rls-rustc 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ed5342b2bbbe8663c04600af506c8902b6b4d3e627b006eb1bd65aa14805f4d" "checksum rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d7c7046dc6a92f2ae02ed302746db4382e75131b9ce20ce967259f6b5867a6a" "checksum rls-vfs 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "be231e1e559c315bc60ced5ad2cc2d7a9c208ed7d4e2c126500149836fda19bb" "checksum rustc-ap-rustc_cratesio_shim 128.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7374a2b466e6e3ce489e045302e304849355faf7fd033d4d95e6e86e48c313b4" @@ -3171,7 +3187,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" -"checksum toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4" "checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" "checksum toml-query 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6854664bfc6df0360c695480836ee90e2d0c965f06db291d10be9344792d43e8" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" diff --git a/src/Cargo.toml b/src/Cargo.toml index 1518e8d910f..7504b43e20c 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -64,4 +64,3 @@ cargo = { path = "tools/cargo" } # RLS depends on `rustfmt` from crates.io, so we put this in a `[patch]` section # for crates.io rustfmt-nightly = { path = "tools/rustfmt" } -clippy_lints = { path = "tools/clippy/clippy_lints" } diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 76d0e6e28ae..6f27402233f 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -268,15 +268,6 @@ fn main() { if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") { cmd.arg(format!("-Clinker={}", host_linker)); } - - if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") { - if s == "true" { - cmd.arg("-C").arg("target-feature=+crt-static"); - } - if s == "false" { - cmd.arg("-C").arg("target-feature=-crt-static"); - } - } } if env::var_os("RUSTC_PARALLEL_QUERIES").is_some() { @@ -297,7 +288,12 @@ fn main() { } if verbose > 1 { - eprintln!("rustc command: {:?}", cmd); + eprintln!( + "rustc command: {:?}={:?} {:?}", + bootstrap::util::dylib_path_var(), + env::join_paths(&dylib_path).unwrap(), + cmd, + ); eprintln!("sysroot: {:?}", sysroot); eprintln!("libdir: {:?}", libdir); } diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 28f5192f2cd..487440becf6 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -489,7 +489,7 @@ class RustBuild(object): """ return os.path.join(self.build_dir, self.build, "stage0") - def get_toml(self, key, section=None): + def get_toml(self, key): """Returns the value of the given key in config.toml, otherwise returns None >>> rb = RustBuild() @@ -501,29 +501,12 @@ class RustBuild(object): >>> rb.get_toml("key3") is None True - - Optionally also matches the section the key appears in - - >>> rb.config_toml = '[a]\\nkey = "value1"\\n[b]\\nkey = "value2"' - >>> rb.get_toml('key', 'a') - 'value1' - >>> rb.get_toml('key', 'b') - 'value2' - >>> rb.get_toml('key', 'c') is None - True """ - - cur_section = None for line in self.config_toml.splitlines(): - section_match = re.match(r'^\s*\[(.*)\]\s*$', line) - if section_match is not None: - cur_section = section_match.group(1) - match = re.match(r'^{}\s*=(.*)$'.format(key), line) if match is not None: value = match.group(1) - if section is None or section == cur_section: - return self.get_string(value) or value.strip() + return self.get_string(value) or value.strip() return None def cargo(self): @@ -606,17 +589,7 @@ class RustBuild(object): env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ (os.pathsep + env["LIBRARY_PATH"]) \ if "LIBRARY_PATH" in env else "" - env["RUSTFLAGS"] = "-Cdebuginfo=2 " - - build_section = "target.{}".format(self.build_triple()) - target_features = [] - if self.get_toml("crt-static", build_section) == "true": - target_features += ["+crt-static"] - elif self.get_toml("crt-static", build_section) == "false": - target_features += ["-crt-static"] - if target_features: - env["RUSTFLAGS"] += "-C target-feature=" + (",".join(target_features)) + " " - + env["RUSTFLAGS"] = "-Cdebuginfo=2" env["PATH"] = os.path.join(self.bin_root(), "bin") + \ os.pathsep + env["PATH"] if not os.path.isfile(self.cargo()): diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index cc1e66332a3..84d29400669 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -584,11 +584,10 @@ impl<'a> Builder<'a> { cargo.env("RUST_CHECK", "1"); } - // If we were invoked from `make` then that's already got a jobserver - // set up for us so no need to tell Cargo about jobs all over again. - if env::var_os("MAKEFLAGS").is_none() && env::var_os("MFLAGS").is_none() { - cargo.arg("-j").arg(self.jobs().to_string()); - } + cargo.arg("-j").arg(self.jobs().to_string()); + // Remove make-related flags to ensure Cargo can correctly set things up + cargo.env_remove("MAKEFLAGS"); + cargo.env_remove("MFLAGS"); // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005 // Force cargo to output binaries with disambiguating hashes in the name @@ -698,10 +697,6 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_CRT_STATIC", x.to_string()); } - if let Some(x) = self.crt_static(compiler.host) { - cargo.env("RUSTC_HOST_CRT_STATIC", x.to_string()); - } - // Enable usage of unstable features cargo.env("RUSTC_BOOTSTRAP", "1"); self.add_rust_test_threads(&mut cargo); @@ -1461,6 +1456,7 @@ mod __test { rustc_args: vec![], fail_fast: true, doc_tests: DocTests::No, + bless: false, }; let build = Build::new(config); diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 3453933a965..a2495f68c1f 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -24,7 +24,7 @@ use Build; use config::Config; // The version number -pub const CFG_RELEASE_NUM: &str = "1.27.0"; +pub const CFG_RELEASE_NUM: &str = "1.28.0"; pub struct GitInfo { inner: Option<Info>, diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 8e0227f8fe1..231ed9d40d2 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -972,7 +972,7 @@ impl Step for Assemble { // Link the compiler binary itself into place let out_dir = builder.cargo_out(build_compiler, Mode::Librustc, host); - let rustc = out_dir.join(exe("rustc", &*host)); + let rustc = out_dir.join(exe("rustc_binary", &*host)); let bindir = sysroot.join("bin"); t!(fs::create_dir_all(&bindir)); let compiler = builder.rustc(target_compiler); diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 5315a3028ff..90dd5d819b0 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -59,6 +59,8 @@ pub enum Subcommand { }, Test { paths: Vec<PathBuf>, + /// Whether to automatically update stderr/stdout files + bless: bool, test_args: Vec<String>, rustc_args: Vec<String>, fail_fast: bool, @@ -173,6 +175,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`"); ); opts.optflag("", "no-doc", "do not run doc tests"); opts.optflag("", "doc", "only run doc tests"); + opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests"); }, "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); }, "clean" => { opts.optflag("", "all", "clean all build artifacts"); }, @@ -258,6 +261,7 @@ Arguments: ./x.py test src/test/run-pass ./x.py test src/libstd --test-args hash_map ./x.py test src/libstd --stage 0 + ./x.py test src/test/ui --bless If no arguments are passed then the complete artifacts for that stage are compiled and tested. @@ -322,6 +326,7 @@ Arguments: "test" => { Subcommand::Test { paths, + bless: matches.opt_present("bless"), test_args: matches.opt_strs("test-args"), rustc_args: matches.opt_strs("rustc-args"), fail_fast: !matches.opt_present("no-fail-fast"), @@ -424,6 +429,13 @@ impl Subcommand { _ => DocTests::Yes, } } + + pub fn bless(&self) -> bool { + match *self { + Subcommand::Test { bless, .. } => bless, + _ => false, + } + } } fn split(s: Vec<String>) -> Vec<String> { diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 1f81a617237..7a4924f03c8 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -47,6 +47,16 @@ pub enum TestKind { Bench, } +impl From<Kind> for TestKind { + fn from(kind: Kind) -> Self { + match kind { + Kind::Test => TestKind::Test, + Kind::Bench => TestKind::Bench, + _ => panic!("unexpected kind in crate: {:?}", kind) + } + } +} + impl TestKind { // Return the cargo subcommand for this test kind fn subcommand(self) -> &'static str { @@ -951,6 +961,10 @@ impl Step for Compiletest { cmd.arg("--host").arg(&*compiler.host); cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.build)); + if builder.config.cmd.bless() { + cmd.arg("--bless"); + } + if let Some(ref nodejs) = builder.config.nodejs { cmd.arg("--nodejs").arg(nodejs); } @@ -1342,13 +1356,7 @@ impl Step for CrateLibrustc { for krate in builder.in_tree_crates("rustc-main") { if run.path.ends_with(&krate.path) { - let test_kind = if builder.kind == Kind::Test { - TestKind::Test - } else if builder.kind == Kind::Bench { - TestKind::Bench - } else { - panic!("unexpected builder.kind in crate: {:?}", builder.kind); - }; + let test_kind = builder.kind.into(); builder.ensure(CrateLibrustc { compiler, @@ -1394,13 +1402,7 @@ impl Step for CrateNotDefault { let builder = run.builder; let compiler = builder.compiler(builder.top_stage, run.host); - let test_kind = if builder.kind == Kind::Test { - TestKind::Test - } else if builder.kind == Kind::Bench { - TestKind::Bench - } else { - panic!("unexpected builder.kind in crate: {:?}", builder.kind); - }; + let test_kind = builder.kind.into(); builder.ensure(CrateNotDefault { compiler, @@ -1461,13 +1463,7 @@ impl Step for Crate { let compiler = builder.compiler(builder.top_stage, run.host); let make = |mode: Mode, krate: &CargoCrate| { - let test_kind = if builder.kind == Kind::Test { - TestKind::Test - } else if builder.kind == Kind::Bench { - TestKind::Bench - } else { - panic!("unexpected builder.kind in crate: {:?}", builder.kind); - }; + let test_kind = builder.kind.into(); builder.ensure(Crate { compiler, @@ -1625,13 +1621,7 @@ impl Step for CrateRustdoc { fn make_run(run: RunConfig) { let builder = run.builder; - let test_kind = if builder.kind == Kind::Test { - TestKind::Test - } else if builder.kind == Kind::Bench { - TestKind::Bench - } else { - panic!("unexpected builder.kind in crate: {:?}", builder.kind); - }; + let test_kind = builder.kind.into(); builder.ensure(CrateRustdoc { host: run.host, diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 4b6e266f1e5..29f37b36e2a 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -431,7 +431,7 @@ impl Step for Rustdoc { // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" // rustdoc a different name. let tool_rustdoc = builder.cargo_out(build_compiler, Mode::Tool, target) - .join(exe("rustdoc-tool-binary", &target_compiler.host)); + .join(exe("rustdoc_tool_binary", &target_compiler.host)); // don't create a stage0-sysroot/bin directory. if target_compiler.stage > 0 { diff --git a/src/doc/grammar.md b/src/doc/grammar.md index 78432b6a965..ee9135b6578 100644 --- a/src/doc/grammar.md +++ b/src/doc/grammar.md @@ -101,29 +101,24 @@ properties: `ident`, `non_null`, `non_eol`, `non_single_quote` and ### Identifiers -The `ident` production is any nonempty Unicode[^non_ascii_idents] string of +The `ident` production is any nonempty Unicode string of the following form: -[^non_ascii_idents]: Non-ASCII characters in identifiers are currently feature - gated. This is expected to improve soon. +- The first character is in one of the following ranges `U+0041` to `U+005A` +("A" to "Z"), `U+0061` to `U+007A` ("a" to "z"), or `U+005F` ("\_"). +- The remaining characters are in the range `U+0030` to `U+0039` ("0" to "9"), +or any of the prior valid initial characters. -- The first character has property `XID_start` -- The remaining characters have property `XID_continue` - -that does _not_ occur in the set of [keywords](#keywords). - -> **Note**: `XID_start` and `XID_continue` as character properties cover the -> character ranges used to form the more familiar C and Java language-family -> identifiers. +as long as the identifier does _not_ occur in the set of [keywords](#keywords). ### Delimiter-restricted productions Some productions are defined by exclusion of particular Unicode characters: - `non_null` is any single Unicode character aside from `U+0000` (null) -- `non_eol` is `non_null` restricted to exclude `U+000A` (`'\n'`) -- `non_single_quote` is `non_null` restricted to exclude `U+0027` (`'`) -- `non_double_quote` is `non_null` restricted to exclude `U+0022` (`"`) +- `non_eol` is any single Unicode character aside from `U+000A` (`'\n'`) +- `non_single_quote` is any single Unicode character aside from `U+0027` (`'`) +- `non_double_quote` is any single Unicode character aside from `U+0022` (`"`) ## Comments diff --git a/src/doc/rustc/src/lints/listing/deny-by-default.md b/src/doc/rustc/src/lints/listing/deny-by-default.md index ef76295f04d..3a85a40fd1f 100644 --- a/src/doc/rustc/src/lints/listing/deny-by-default.md +++ b/src/doc/rustc/src/lints/listing/deny-by-default.md @@ -91,43 +91,6 @@ The legacy_directory_ownership warning is issued when The warning can be fixed by renaming the parent module to "mod.rs" and moving it into its own directory if appropriate. -## legacy-imports - -This lint detects names that resolve to ambiguous glob imports. Some example -code that triggers this lint: - -```rust,ignore -pub struct Foo; - -mod bar { - struct Foo; - - mod baz { - use *; - use bar::*; - fn f(_: Foo) {} - } -} -``` - -This will produce: - -```text -error: `Foo` is ambiguous - --> src/main.rs:9:17 - | -7 | use *; - | - `Foo` could refer to the name imported here -8 | use bar::*; - | ------ `Foo` could also refer to the name imported here -9 | fn f(_: Foo) {} - | ^^^ - | - = note: #[deny(legacy_imports)] on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #38260 <https://github.com/rust-lang/rust/issues/38260> -``` - ## missing-fragment-specifier diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index 3098587a8a4..fd7d1713ca5 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -268,10 +268,10 @@ not actually pass as a test. # fn foo() {} ``` -`compile_fail` tells `rustdoc` that the compilation should fail. If it -compiles, then the test will fail. However please note that code failing -with the current Rust release may work in a future release, as new features -are added. +The `no_run` attribute will compile your code, but not run it. This is +important for examples such as "Here's how to retrieve a web page," +which you would want to ensure compiles, but might be run in a test +environment that has no network access. ```text /// ```compile_fail @@ -280,7 +280,7 @@ are added. /// ``` ``` -The `no_run` attribute will compile your code, but not run it. This is -important for examples such as "Here's how to retrieve a web page," -which you would want to ensure compiles, but might be run in a test -environment that has no network access. +`compile_fail` tells `rustdoc` that the compilation should fail. If it +compiles, then the test will fail. However please note that code failing +with the current Rust release may work in a future release, as new features +are added. diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index 87f3a0c765c..320283f31b5 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1,3 +1,3 @@ % The Rust Tutorial -This tutorial has been deprecated in favor of [the Book](book/index.html). Go check that out instead! +This tutorial has been deprecated in favor of [the Book](book/index.html), which is available free online and in dead tree form. Go check that out instead! diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml index 6383bd1e941..ada21e04b30 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -2,6 +2,8 @@ authors = ["The Rust Project Developers"] name = "alloc" version = "0.0.0" +autotests = false +autobenches = false [lib] name = "alloc" diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 09d16b26520..79607b06f94 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -22,28 +22,6 @@ use core::usize; #[doc(inline)] pub use core::alloc::*; -#[cfg(stage0)] -extern "Rust" { - #[allocator] - #[rustc_allocator_nounwind] - fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8; - #[cold] - #[rustc_allocator_nounwind] - fn __rust_oom(err: *const u8) -> !; - #[rustc_allocator_nounwind] - fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); - #[rustc_allocator_nounwind] - fn __rust_realloc(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - err: *mut u8) -> *mut u8; - #[rustc_allocator_nounwind] - fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8; -} - -#[cfg(not(stage0))] extern "Rust" { #[allocator] #[rustc_allocator_nounwind] @@ -74,10 +52,7 @@ pub const Heap: Global = Global; unsafe impl GlobalAlloc for Global { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { - #[cfg(not(stage0))] let ptr = __rust_alloc(layout.size(), layout.align()); - #[cfg(stage0)] - let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0); ptr as *mut Opaque } @@ -88,20 +63,13 @@ unsafe impl GlobalAlloc for Global { #[inline] unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { - #[cfg(not(stage0))] let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size); - #[cfg(stage0)] - let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), - new_size, layout.align(), &mut 0); ptr as *mut Opaque } #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { - #[cfg(not(stage0))] let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); - #[cfg(stage0)] - let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); ptr as *mut Opaque } } @@ -152,14 +120,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { } } -#[cfg(stage0)] -#[lang = "box_free"] -#[inline] -unsafe fn old_box_free<T: ?Sized>(ptr: *mut T) { - box_free(Unique::new_unchecked(ptr)) -} - -#[cfg_attr(not(any(test, stage0)), lang = "box_free")] +#[cfg_attr(not(test), lang = "box_free")] #[inline] pub(crate) unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) { let ptr = ptr.as_ptr(); @@ -172,12 +133,6 @@ pub(crate) unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) { } } -#[cfg(stage0)] -pub fn oom() -> ! { - unsafe { ::core::intrinsics::abort() } -} - -#[cfg(not(stage0))] pub fn oom() -> ! { extern { #[lang = "oom"] diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f7dd9d4f010..91de3ad0c39 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -75,7 +75,6 @@ #![deny(missing_debug_implementations)] #![cfg_attr(test, allow(deprecated))] // rand -#![cfg_attr(all(not(test), stage0), feature(float_internals))] #![cfg_attr(not(test), feature(exact_size_is_empty))] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(rand, test))] @@ -90,13 +89,10 @@ #![feature(collections_range)] #![feature(const_fn)] #![feature(core_intrinsics)] -#![cfg_attr(stage0, feature(core_slice_ext))] -#![cfg_attr(stage0, feature(core_str_ext))] #![feature(custom_attribute)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] #![feature(fmt_internals)] -#![cfg_attr(stage0, feature(fn_must_use))] #![feature(from_ref)] #![feature(fundamental)] #![feature(lang_items)] @@ -122,7 +118,6 @@ #![feature(exact_chunks)] #![feature(pointer_methods)] #![feature(inclusive_range_methods)] -#![cfg_attr(stage0, feature(generic_param_attrs))] #![feature(rustc_const_unstable)] #![feature(const_vec_new)] @@ -157,15 +152,10 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] /// Use the `alloc` module instead. -#[cfg(not(stage0))] pub mod heap { pub use alloc::*; } -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -#[cfg(stage0)] -pub mod heap; // Primitive types using the heaps above diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 6caf12aa7eb..4427ac004f9 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -101,7 +101,6 @@ use core::cmp::Ordering::{self, Less}; use core::mem::size_of; use core::mem; use core::ptr; -#[cfg(stage0)] use core::slice::SliceExt; use core::{u8, u16, u32}; use borrow::{Borrow, BorrowMut, ToOwned}; @@ -171,13 +170,9 @@ mod hack { } } -#[cfg_attr(stage0, lang = "slice")] -#[cfg_attr(not(stage0), lang = "slice_alloc")] +#[lang = "slice_alloc"] #[cfg(not(test))] impl<T> [T] { - #[cfg(stage0)] - slice_core_methods!(); - /// Sorts the slice. /// /// This sort is stable (i.e. does not reorder equal elements) and `O(n log n)` worst-case. @@ -467,8 +462,7 @@ impl<T> [T] { } } -#[cfg_attr(stage0, lang = "slice_u8")] -#[cfg_attr(not(stage0), lang = "slice_u8_alloc")] +#[lang = "slice_u8_alloc"] #[cfg(not(test))] impl [u8] { /// Returns a vector containing a copy of this slice where each byte @@ -504,9 +498,6 @@ impl [u8] { me.make_ascii_lowercase(); me } - - #[cfg(stage0)] - slice_u8_core_methods!(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 42efdea74b1..c10c0a69433 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -40,7 +40,6 @@ use core::fmt; use core::str as core_str; -#[cfg(stage0)] use core::str::StrExt; use core::str::pattern::Pattern; use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; @@ -158,13 +157,9 @@ impl ToOwned for str { } /// Methods for string slices. -#[cfg_attr(stage0, lang = "str")] -#[cfg_attr(not(stage0), lang = "str_alloc")] +#[lang = "str_alloc"] #[cfg(not(test))] impl str { - #[cfg(stage0)] - str_core_methods!(); - /// Converts a `Box<str>` into a `Box<[u8]>` without copying or allocating. /// /// # Examples diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 1c8ff316e55..081c473768f 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -25,7 +25,6 @@ #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(exact_chunks)] -#![feature(inclusive_range_methods)] extern crate alloc_system; extern crate core; diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index d30f8cd0fca..bf89b377b7e 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -73,9 +73,6 @@ use core::intrinsics::{arith_offset, assume}; use core::iter::{FromIterator, FusedIterator, TrustedLen}; use core::marker::PhantomData; use core::mem; -#[cfg(not(test))] -#[cfg(stage0)] -use core::num::Float; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{Index, IndexMut, RangeBounds}; use core::ops; diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 4b8755877de..ce856eccd83 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -97,13 +97,6 @@ mod contents { ptr } - #[cfg(stage0)] - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_oom() -> ! { - ::core::intrinsics::abort(); - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_dealloc(ptr: *mut u8, diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 7376ac0f15d..9490b54e675 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -73,33 +73,6 @@ unsafe impl Alloc for System { } } -#[cfg(stage0)] -#[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl<'a> Alloc for &'a System { - #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<Opaque>, AllocErr> { - NonNull::new(GlobalAlloc::alloc(*self, layout)).ok_or(AllocErr) - } - - #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<Opaque>, AllocErr> { - NonNull::new(GlobalAlloc::alloc_zeroed(*self, layout)).ok_or(AllocErr) - } - - #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull<Opaque>, layout: Layout) { - GlobalAlloc::dealloc(*self, ptr.as_ptr(), layout) - } - - #[inline] - unsafe fn realloc(&mut self, - ptr: NonNull<Opaque>, - layout: Layout, - new_size: usize) -> Result<NonNull<Opaque>, AllocErr> { - NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) - } -} - #[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] mod realloc_fallback { use core::alloc::{GlobalAlloc, Opaque, Layout}; diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index c79e0e14e3d..f7143a4f981 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -26,7 +26,6 @@ #![feature(alloc)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] -#![cfg_attr(stage0, feature(generic_param_attrs))] #![cfg_attr(test, feature(test))] #![allow(deprecated)] diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml index 24529f7a9d8..321ed892ea9 100644 --- a/src/libcore/Cargo.toml +++ b/src/libcore/Cargo.toml @@ -2,6 +2,8 @@ authors = ["The Rust Project Developers"] name = "core" version = "0.0.0" +autotests = false +autobenches = false [lib] name = "core" diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index f79f7351698..3b15ba2b4ab 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -153,7 +153,6 @@ pub struct AssertParamIsCopy<T: Copy + ?Sized> { _field: ::marker::PhantomData<T /// /// Implementations that cannot be described in Rust /// are implemented in `SelectionContext::copy_clone_conditions()` in librustc. -#[cfg(not(stage0))] mod impls { use super::Clone; diff --git a/src/libcore/internal_macros.rs b/src/libcore/internal_macros.rs index 58eef649287..db75f9bf210 100644 --- a/src/libcore/internal_macros.rs +++ b/src/libcore/internal_macros.rs @@ -86,17 +86,3 @@ macro_rules! forward_ref_op_assign { } } } - -#[cfg(stage0)] -macro_rules! public_in_stage0 { - ( { $(#[$attr:meta])* } $($Item: tt)*) => { - $(#[$attr])* pub $($Item)* - } -} - -#[cfg(not(stage0))] -macro_rules! public_in_stage0 { - ( { $(#[$attr:meta])* } $($Item: tt)*) => { - $(#[$attr])* pub(crate) $($Item)* - } -} diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 5ec6cb6c710..2e3f5cb65c9 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -962,122 +962,59 @@ extern "rust-intrinsic" { /// value is not necessarily valid to be used to actually access memory. pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; - /// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source - /// and destination must *not* overlap. + /// Copies `count * size_of<T>` bytes from `src` to `dst`. The source + /// and destination may *not* overlap. /// - /// For regions of memory which might overlap, use [`copy`] instead. - /// - /// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`]. - /// - /// [`copy`]: ./fn.copy.html - /// [`memcpy`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memcpy + /// `copy_nonoverlapping` is semantically equivalent to C's `memcpy`. /// /// # Safety /// - /// Behavior is undefined if any of the following conditions are violated: - /// - /// * The region of memory which begins at `src` and has a length of - /// `count * size_of::<T>()` bytes must be *both* valid and initialized. - /// - /// * The region of memory which begins at `dst` and has a length of - /// `count * size_of::<T>()` bytes must be valid (but may or may not be - /// initialized). - /// - /// * The two regions of memory must *not* overlap. - /// - /// * `src` must be properly aligned. - /// - /// * `dst` must be properly aligned. - /// - /// Additionally, if `T` is not [`Copy`], only the region at `src` *or* the - /// region at `dst` can be used or dropped after calling - /// `copy_nonoverlapping`. `copy_nonoverlapping` creates bitwise copies of - /// `T`, regardless of whether `T: Copy`, which can result in undefined - /// behavior if both copies are used. - /// - /// [`Copy`]: ../marker/trait.Copy.html + /// Beyond requiring that the program must be allowed to access both regions + /// of memory, it is Undefined Behavior for source and destination to + /// overlap. Care must also be taken with the ownership of `src` and + /// `dst`. This method semantically moves the values of `src` into `dst`. + /// However it does not drop the contents of `dst`, or prevent the contents + /// of `src` from being dropped or used. /// /// # Examples /// - /// Manually implement [`Vec::append`]: + /// A safe swap function: /// /// ``` + /// use std::mem; /// use std::ptr; /// - /// /// Moves all the elements of `src` into `dst`, leaving `src` empty. - /// fn append<T>(dst: &mut Vec<T>, src: &mut Vec<T>) { - /// let src_len = src.len(); - /// let dst_len = dst.len(); - /// - /// // Ensure that `dst` has enough capacity to hold all of `src`. - /// dst.reserve(src_len); - /// + /// # #[allow(dead_code)] + /// fn swap<T>(x: &mut T, y: &mut T) { /// unsafe { - /// // The call to offset is always safe because `Vec` will never - /// // allocate more than `isize::MAX` bytes. - /// let dst = dst.as_mut_ptr().offset(dst_len as isize); - /// let src = src.as_ptr(); - /// - /// // The two regions cannot overlap becuase mutable references do - /// // not alias, and two different vectors cannot own the same - /// // memory. - /// ptr::copy_nonoverlapping(src, dst, src_len); - /// } + /// // Give ourselves some scratch space to work with + /// let mut t: T = mem::uninitialized(); /// - /// unsafe { - /// // Truncate `src` without dropping its contents. - /// src.set_len(0); + /// // Perform the swap, `&mut` pointers never alias + /// ptr::copy_nonoverlapping(x, &mut t, 1); + /// ptr::copy_nonoverlapping(y, x, 1); + /// ptr::copy_nonoverlapping(&t, y, 1); /// - /// // Notify `dst` that it now holds the contents of `src`. - /// dst.set_len(dst_len + src_len); + /// // y and t now point to the same thing, but we need to completely forget `t` + /// // because it's no longer relevant. + /// mem::forget(t); /// } /// } - /// - /// let mut a = vec!['r']; - /// let mut b = vec!['u', 's', 't']; - /// - /// append(&mut a, &mut b); - /// - /// assert_eq!(a, &['r', 'u', 's', 't']); - /// assert!(b.is_empty()); /// ``` - /// - /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append #[stable(feature = "rust1", since = "1.0.0")] pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize); - /// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source + /// Copies `count * size_of<T>` bytes from `src` to `dst`. The source /// and destination may overlap. /// - /// If the source and destination will *never* overlap, - /// [`copy_nonoverlapping`] can be used instead. - /// - /// `copy` is semantically equivalent to C's [`memmove`]. - /// - /// [`copy_nonoverlapping`]: ./fn.copy_nonoverlapping.html - /// [`memmove`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memmove + /// `copy` is semantically equivalent to C's `memmove`. /// /// # Safety /// - /// Behavior is undefined if any of the following conditions are violated: - /// - /// * The region of memory which begins at `src` and has a length of - /// `count * size_of::<T>()` bytes must be *both* valid and initialized. - /// - /// * The region of memory which begins at `dst` and has a length of - /// `count * size_of::<T>()` bytes must be valid (but may or may not be - /// initialized). - /// - /// * `src` must be properly aligned. - /// - /// * `dst` must be properly aligned. - /// - /// Additionally, if `T` is not [`Copy`], only the region at `src` *or* the - /// region at `dst` can be used or dropped after calling `copy`. `copy` - /// creates bitwise copies of `T`, regardless of whether `T: Copy`, which - /// can result in undefined behavior if both copies are used. - /// - /// [`Copy`]: ../marker/trait.Copy.html + /// Care must be taken with the ownership of `src` and `dst`. + /// This method semantically moves the values of `src` into `dst`. + /// However it does not drop the contents of `dst`, or prevent the contents of `src` + /// from being dropped or used. /// /// # Examples /// @@ -1094,34 +1031,15 @@ extern "rust-intrinsic" { /// dst /// } /// ``` + /// #[stable(feature = "rust1", since = "1.0.0")] pub fn copy<T>(src: *const T, dst: *mut T, count: usize); - /// Sets `count * size_of::<T>()` bytes of memory starting at `dst` to - /// `val`. - /// - /// `write_bytes` is semantically equivalent to C's [`memset`]. - /// - /// [`memset`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memset - /// - /// # Safety - /// - /// Behavior is undefined if any of the following conditions are violated: - /// - /// * The region of memory which begins at `dst` and has a length of - /// `count` bytes must be valid. - /// - /// * `dst` must be properly aligned. - /// - /// Additionally, the caller must ensure that writing `count` bytes to the - /// given region of memory results in a valid value of `T`. Creating an - /// invalid value of `T` can result in undefined behavior. An example is - /// provided below. + /// Invokes memset on the specified pointer, setting `count * size_of::<T>()` + /// bytes of memory starting at `dst` to `val`. /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::ptr; /// @@ -1132,23 +1050,6 @@ extern "rust-intrinsic" { /// } /// assert_eq!(vec, [b'a', b'a', 0, 0]); /// ``` - /// - /// Creating an invalid value: - /// - /// ```no_run - /// use std::{mem, ptr}; - /// - /// let mut v = Box::new(0i32); - /// - /// unsafe { - /// // Leaks the previously held value by overwriting the `Box<T>` with - /// // a null pointer. - /// ptr::write_bytes(&mut v, 0, mem::size_of::<Box<i32>>()); - /// } - /// - /// // At this point, using or dropping `v` results in undefined behavior. - /// // v = Box::new(0i32); // ERROR - /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn write_bytes<T>(dst: *mut T, val: u8, count: usize); @@ -1463,38 +1364,8 @@ extern "rust-intrinsic" { /// source as well as std's catch implementation. pub fn try(f: fn(*mut u8), data: *mut u8, local_ptr: *mut u8) -> i32; - /// Computes the byte offset that needs to be applied to `ptr` in order to - /// make it aligned to `align`. - /// If it is not possible to align `ptr`, the implementation returns - /// `usize::max_value()`. - /// - /// There are no guarantees whatsover that offsetting the pointer will not - /// overflow or go beyond the allocation that `ptr` points into. - /// It is up to the caller to ensure that the returned offset is correct - /// in all terms other than alignment. - /// - /// # Examples - /// - /// Accessing adjacent `u8` as `u16` - /// - /// ``` - /// # #![feature(core_intrinsics)] - /// # fn foo(n: usize) { - /// # use std::intrinsics::align_offset; - /// # use std::mem::align_of; - /// # unsafe { - /// let x = [5u8, 6u8, 7u8, 8u8, 9u8]; - /// let ptr = &x[n] as *const u8; - /// let offset = align_offset(ptr as *const (), align_of::<u16>()); - /// if offset < x.len() - n - 1 { - /// let u16_ptr = ptr.offset(offset as isize) as *const u16; - /// assert_ne!(*u16_ptr, 500); - /// } else { - /// // while the pointer can be aligned via `offset`, it would point - /// // outside the allocation - /// } - /// # } } - /// ``` + #[cfg(stage0)] + /// docs my friends, its friday! pub fn align_offset(ptr: *const (), align: usize) -> usize; /// Emits a `!nontemporal` store according to LLVM (see their docs). diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 06fbfcecba8..77b5488084d 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -112,18 +112,13 @@ #![feature(unwind_attributes)] #![feature(doc_alias)] #![feature(inclusive_range_methods)] - -#![cfg_attr(not(stage0), feature(mmx_target_feature))] -#![cfg_attr(not(stage0), feature(tbm_target_feature))] -#![cfg_attr(not(stage0), feature(sse4a_target_feature))] -#![cfg_attr(not(stage0), feature(arm_target_feature))] -#![cfg_attr(not(stage0), feature(powerpc_target_feature))] -#![cfg_attr(not(stage0), feature(mips_target_feature))] -#![cfg_attr(not(stage0), feature(aarch64_target_feature))] - -#![cfg_attr(stage0, feature(target_feature))] -#![cfg_attr(stage0, feature(cfg_target_feature))] -#![cfg_attr(stage0, feature(fn_must_use))] +#![feature(mmx_target_feature)] +#![feature(tbm_target_feature)] +#![feature(sse4a_target_feature)] +#![feature(arm_target_feature)] +#![feature(powerpc_target_feature)] +#![feature(mips_target_feature)] +#![feature(aarch64_target_feature)] #[prelude_import] #[allow(unused)] diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index db5f50a99ca..6c8ee0eda11 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -611,7 +611,6 @@ pub unsafe auto trait Unpin {} /// /// Implementations that cannot be described in Rust /// are implemented in `SelectionContext::copy_clone_conditions()` in librustc. -#[cfg(not(stage0))] mod copy_impls { use super::Copy; diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 4a7dc13f0f2..718dd42a615 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -19,7 +19,7 @@ use mem; use num::Float; -#[cfg(not(stage0))] use num::FpCategory; +use num::FpCategory; use num::FpCategory as Fp; /// The radix or base of the internal representation of `f32`. @@ -277,7 +277,6 @@ impl Float for f32 { // FIXME: remove (inline) this macro and the Float trait // when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] #[unstable(feature = "core_float", issue = "32110")] macro_rules! f32_core_methods { () => { /// Returns `true` if this value is `NaN` and false otherwise. @@ -553,7 +552,6 @@ macro_rules! f32_core_methods { () => { #[lang = "f32"] #[cfg(not(test))] -#[cfg(not(stage0))] impl f32 { f32_core_methods!(); } diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 801de5e87bd..f128c55c78a 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -19,7 +19,7 @@ use mem; use num::Float; -#[cfg(not(stage0))] use num::FpCategory; +use num::FpCategory; use num::FpCategory as Fp; /// The radix or base of the internal representation of `f64`. @@ -276,7 +276,6 @@ impl Float for f64 { // FIXME: remove (inline) this macro and the Float trait // when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] #[unstable(feature = "core_float", issue = "32110")] macro_rules! f64_core_methods { () => { /// Returns `true` if this value is `NaN` and false otherwise. @@ -562,7 +561,6 @@ macro_rules! f64_core_methods { () => { #[lang = "f64"] #[cfg(not(test))] -#[cfg(not(stage0))] impl f64 { f64_core_methods!(); } diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 6df8ca98ba9..58d45b107f1 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -422,7 +422,6 @@ $EndFeature, " /// assert_eq!(m, -22016); /// ``` #[unstable(feature = "reverse_bits", issue = "48763")] - #[cfg(not(stage0))] #[inline] pub fn reverse_bits(self) -> Self { (self as $UnsignedT).reverse_bits() as Self @@ -2194,7 +2193,6 @@ assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " /// assert_eq!(m, 43520); /// ``` #[unstable(feature = "reverse_bits", issue = "48763")] - #[cfg(not(stage0))] #[inline] pub fn reverse_bits(self) -> Self { unsafe { intrinsics::bitreverse(self as $ActualT) as Self } diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 697e6a3efde..7c6e2447bdb 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -318,8 +318,6 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> { /// # Examples /// /// ``` -/// #![feature(inclusive_range_methods)] -/// /// assert_eq!((3..=5), std::ops::RangeInclusive::new(3, 5)); /// assert_eq!(3 + 4 + 5, (3..=5).sum()); /// @@ -335,18 +333,8 @@ pub struct RangeInclusive<Idx> { // but it is known that LLVM is not able to optimize loops following that RFC. // Consider adding an extra `bool` field to indicate emptiness of the range. // See #45222 for performance test cases. - #[cfg(not(stage0))] pub(crate) start: Idx, - #[cfg(not(stage0))] pub(crate) end: Idx, - /// The lower bound of the range (inclusive). - #[cfg(stage0)] - #[unstable(feature = "inclusive_range_fields", issue = "49022")] - pub start: Idx, - /// The upper bound of the range (inclusive). - #[cfg(stage0)] - #[unstable(feature = "inclusive_range_fields", issue = "49022")] - pub end: Idx, } impl<Idx> RangeInclusive<Idx> { @@ -355,12 +343,11 @@ impl<Idx> RangeInclusive<Idx> { /// # Examples /// /// ``` - /// #![feature(inclusive_range_methods)] /// use std::ops::RangeInclusive; /// /// assert_eq!(3..=5, RangeInclusive::new(3, 5)); /// ``` - #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] #[inline] pub const fn new(start: Idx, end: Idx) -> Self { Self { start, end } @@ -373,17 +360,18 @@ impl<Idx> RangeInclusive<Idx> { /// whether the inclusive range is empty, use the [`is_empty()`] method /// instead of comparing `start() > end()`. /// + /// Note: the value returned by this method is unspecified after the range + /// has been iterated to exhaustion. + /// /// [`end()`]: #method.end /// [`is_empty()`]: #method.is_empty /// /// # Examples /// /// ``` - /// #![feature(inclusive_range_methods)] - /// /// assert_eq!((3..=5).start(), &3); /// ``` - #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] #[inline] pub fn start(&self) -> &Idx { &self.start @@ -396,32 +384,34 @@ impl<Idx> RangeInclusive<Idx> { /// whether the inclusive range is empty, use the [`is_empty()`] method /// instead of comparing `start() > end()`. /// + /// Note: the value returned by this method is unspecified after the range + /// has been iterated to exhaustion. + /// /// [`start()`]: #method.start /// [`is_empty()`]: #method.is_empty /// /// # Examples /// /// ``` - /// #![feature(inclusive_range_methods)] - /// /// assert_eq!((3..=5).end(), &5); /// ``` - #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] #[inline] pub fn end(&self) -> &Idx { &self.end } - /// Destructures the RangeInclusive into (lower bound, upper (inclusive) bound). + /// Destructures the `RangeInclusive` into (lower bound, upper (inclusive) bound). + /// + /// Note: the value returned by this method is unspecified after the range + /// has been iterated to exhaustion. /// /// # Examples /// /// ``` - /// #![feature(inclusive_range_methods)] - /// /// assert_eq!((3..=5).into_inner(), (3, 5)); /// ``` - #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] #[inline] pub fn into_inner(self) -> (Idx, Idx) { (self.start, self.end) diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 0dfdabee031..28f37f72d6f 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -705,6 +705,42 @@ impl<T> Option<T> { } } + /// Returns [`Some`] if exactly one of `self`, `optb` is [`Some`], otherwise returns `None`. + /// + /// [`Some`]: #variant.Some + /// [`None`]: #variant.None + /// + /// # Examples + /// + /// ``` + /// #![feature(option_xor)] + /// + /// let x = Some(2); + /// let y: Option<u32> = None; + /// assert_eq!(x.xor(y), Some(2)); + /// + /// let x: Option<u32> = None; + /// let y = Some(2); + /// assert_eq!(x.xor(y), Some(2)); + /// + /// let x = Some(2); + /// let y = Some(2); + /// assert_eq!(x.xor(y), None); + /// + /// let x: Option<u32> = None; + /// let y: Option<u32> = None; + /// assert_eq!(x.xor(y), None); + /// ``` + #[inline] + #[unstable(feature = "option_xor", issue = "50512")] + pub fn xor(self, optb: Option<T>) -> Option<T> { + match (self, optb) { + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + _ => None, + } + } + ///////////////////////////////////////////////////////////////////////// // Entry-like operations to insert if None and return a reference ///////////////////////////////////////////////////////////////////////// diff --git a/src/libcore/prelude/v1.rs b/src/libcore/prelude/v1.rs index 8212648f2d8..45f629a6442 100644 --- a/src/libcore/prelude/v1.rs +++ b/src/libcore/prelude/v1.rs @@ -54,13 +54,3 @@ pub use option::Option::{self, Some, None}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use result::Result::{self, Ok, Err}; - -// Re-exported extension traits for primitive types -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -#[cfg(stage0)] -pub use slice::SliceExt; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -#[cfg(stage0)] -pub use str::StrExt; diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 63bcc024020..6c0709caa08 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -10,7 +10,7 @@ // FIXME: talk about offset, copy_memory, copy_nonoverlapping_memory -//! Manually manage memory through raw pointers. +//! Raw, unsafe pointers, `*const T`, and `*mut T`. //! //! *[See also the pointer primitive types](../../std/primitive.pointer.html).* @@ -38,62 +38,21 @@ pub use intrinsics::write_bytes; /// Executes the destructor (if any) of the pointed-to value. /// -/// This is semantically equivalent to calling [`ptr::read`] and discarding -/// the result, but has the following advantages: +/// This has two use cases: /// /// * It is *required* to use `drop_in_place` to drop unsized types like /// trait objects, because they can't be read out onto the stack and /// dropped normally. /// -/// * It is friendlier to the optimizer to do this over [`ptr::read`] when +/// * It is friendlier to the optimizer to do this over `ptr::read` when /// dropping manually allocated memory (e.g. when writing Box/Rc/Vec), /// as the compiler doesn't need to prove that it's sound to elide the /// copy. /// -/// [`ptr::read`]: ../ptr/fn.read.html -/// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `to_drop` must point to valid memory. -/// -/// * `to_drop` must be properly aligned. -/// -/// Additionally, if `T` is not [`Copy`], using the pointed-to value after -/// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop = -/// foo` counts as a use because it will cause the the value to be dropped -/// again. [`write`] can be used to overwrite data without causing it to be -/// dropped. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`write`]: ../ptr/fn.write.html -/// -/// # Examples -/// -/// Manually remove the last item from a vector: -/// -/// ``` -/// use std::ptr; -/// use std::rc::Rc; -/// -/// let last = Rc::new(1); -/// let weak = Rc::downgrade(&last); -/// -/// let mut v = vec![Rc::new(0), last]; -/// -/// unsafe { -/// // Without a call `drop_in_place`, the last item would never be dropped, -/// // and the memory it manages would be leaked. -/// ptr::drop_in_place(&mut v[1]); -/// v.set_len(1); -/// } -/// -/// assert_eq!(v, &[0.into()]); -/// -/// // Ensure that the last item was dropped. -/// assert!(weak.upgrade().is_none()); -/// ``` +/// This has all the same safety problems as `ptr::read` with respect to +/// invalid pointers, types, and double drops. #[stable(feature = "drop_in_place", since = "1.8.0")] #[lang = "drop_in_place"] #[allow(unconditional_recursion)] @@ -134,25 +93,17 @@ pub const fn null_mut<T>() -> *mut T { 0 as *mut T } /// Swaps the values at two mutable locations of the same type, without /// deinitializing either. /// -/// But for the following two exceptions, this function is semantically -/// equivalent to [`mem::swap`]: -/// -/// * It operates on raw pointers instead of references. When references are -/// available, [`mem::swap`] should be preferred. -/// -/// * The two pointed-to values may overlap. If the values do overlap, then the -/// overlapping region of memory from `x` will be used. This is demonstrated -/// in the examples below. -/// -/// [`mem::swap`]: ../mem/fn.swap.html +/// The values pointed at by `x` and `y` may overlap, unlike `mem::swap` which +/// is otherwise equivalent. If the values do overlap, then the overlapping +/// region of memory from `x` will be used. This is demonstrated in the +/// examples section below. /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: +/// This function copies the memory through the raw pointers passed to it +/// as arguments. /// -/// * `x` and `y` must point to valid, initialized memory. -/// -/// * `x` and `y` must be properly aligned. +/// Ensure that these pointers are valid before calling `swap`. /// /// # Examples /// @@ -288,39 +239,13 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { } } -/// Replaces the value at `dest` with `src`, returning the old value, without -/// dropping either. -/// -/// This function is semantically equivalent to [`mem::replace`] except that it -/// operates on raw pointers instead of references. When references are -/// available, [`mem::replace`] should be preferred. -/// -/// [`mem::replace`]: ../mem/fn.replace.html +/// Replaces the value at `dest` with `src`, returning the old +/// value, without dropping either. /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dest` must point to valid, initialized memory. -/// -/// * `dest` must be properly aligned. -/// -/// # Examples -/// -/// ``` -/// use std::ptr; -/// -/// let mut rust = vec!['b', 'u', 's', 't']; -/// -/// // `mem::replace` would have the same effect without requiring the unsafe -/// // block. -/// let b = unsafe { -/// ptr::replace(&mut rust[0], 'r') -/// }; -/// -/// assert_eq!(b, 'b'); -/// assert_eq!(rust, &['r', 'u', 's', 't']); -/// ``` +/// This is only unsafe because it accepts a raw pointer. +/// Otherwise, this operation is identical to `mem::replace`. #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn replace<T>(dest: *mut T, mut src: T) -> T { @@ -333,23 +258,14 @@ pub unsafe fn replace<T>(dest: *mut T, mut src: T) -> T { /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must point to valid, initialized memory. +/// 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`, +/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use +/// because it will attempt to drop the value previously at `*src`. /// -/// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the -/// case. -/// -/// Additionally, if `T` is not [`Copy`], only the returned value *or* the -/// pointed-to value can be used or dropped after calling `read`. `read` creates -/// a bitwise copy of `T`, regardless of whether `T: Copy`, which can result -/// in undefined behavior if both copies are used. Note that `*src = foo` counts -/// as a use because it will attempt to drop the value previously at `*src`. -/// [`write`] can be used to overwrite data without causing it to be dropped. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read_unaligned`]: ./fn.read_unaligned.html -/// [`write`]: ./fn.write.html +/// The pointer must be aligned; use `read_unaligned` if that is not the case. /// /// # Examples /// @@ -363,44 +279,6 @@ pub unsafe fn replace<T>(dest: *mut T, mut src: T) -> T { /// assert_eq!(std::ptr::read(y), 12); /// } /// ``` -/// -/// Manually implement [`mem::swap`]: -/// -/// ``` -/// use std::ptr; -/// -/// fn swap<T>(a: &mut T, b: &mut T) { -/// unsafe { -/// // Create a bitwise copy of the value at `a` in `tmp`. -/// let tmp = ptr::read(a); -/// -/// // Exiting at this point (either by explicitly returning or by -/// // calling a function which panics) would cause the value in `tmp` to -/// // be dropped while the same value is still referenced by `a`. This -/// // could trigger undefined behavior if `T` is not `Copy`. -/// -/// // Create a bitwise copy of the value at `b` in `a`. -/// // This is safe because mutable references cannot alias. -/// ptr::copy_nonoverlapping(b, a, 1); -/// -/// // As above, exiting here could trigger undefined behavior because -/// // the same value is referenced by `a` and `b`. -/// -/// // Move `tmp` into `b`. -/// ptr::write(b, tmp); -/// } -/// } -/// -/// let mut foo = "foo".to_owned(); -/// let mut bar = "bar".to_owned(); -/// -/// swap(&mut foo, &mut bar); -/// -/// assert_eq!(foo, "bar"); -/// assert_eq!(bar, "foo"); -/// ``` -/// -/// [`mem::swap`]: ../mem/fn.swap.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn read<T>(src: *const T) -> T { @@ -412,62 +290,28 @@ pub unsafe fn read<T>(src: *const T) -> T { /// Reads the value from `src` without moving it. This leaves the /// memory in `src` unchanged. /// -/// Unlike [`read`], `read_unaligned` works with unaligned pointers. -/// -/// [`read`]: ./fn.read.html +/// Unlike `read`, the pointer may be unaligned. /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must point to valid, initialized memory. -/// -/// Additionally, if `T` is not [`Copy`], only the returned value *or* the -/// pointed-to value can be used or dropped after calling `read_unaligned`. -/// `read_unaligned` creates a bitwise copy of `T`, regardless of whether `T: -/// Copy`, and this can result in undefined behavior if both copies are used. -/// Note that `*src = foo` counts as a use because it will attempt to drop the -/// value previously at `*src`. [`write_unaligned`] can be used to overwrite -/// data without causing it to be dropped. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`write_unaligned`]: ./fn.write_unaligned.html +/// 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`, +/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use +/// because it will attempt to drop the value previously at `*src`. /// /// # Examples /// -/// Access members of a packed struct by reference: +/// Basic usage: /// /// ``` -/// use std::ptr; +/// let x = 12; +/// let y = &x as *const i32; /// -/// #[repr(packed, C)] -/// #[derive(Default)] -/// struct Packed { -/// _padding: u8, -/// unaligned: u32, +/// unsafe { +/// assert_eq!(std::ptr::read_unaligned(y), 12); /// } -/// -/// let x = Packed { -/// _padding: 0x00, -/// unaligned: 0x01020304, -/// }; -/// -/// let v = unsafe { -/// // Take a reference to a 32-bit integer which is not aligned. -/// let unaligned = &x.unaligned; -/// -/// // Dereferencing normally will emit an unaligned load instruction, -/// // causing undefined behavior. -/// // let v = *unaligned; // ERROR -/// -/// // Instead, use `read_unaligned` to read improperly aligned values. -/// let v = ptr::read_unaligned(unaligned); -/// -/// v -/// }; -/// -/// // Accessing unaligned values directly is safe. -/// assert!(x.unaligned == v); /// ``` #[inline] #[stable(feature = "ptr_unaligned", since = "1.17.0")] @@ -482,7 +326,11 @@ pub unsafe fn read_unaligned<T>(src: *const T) -> T { /// Overwrites a memory location with the given value without reading or /// dropping the old value. /// -/// `write` does not drop the contents of `dst`. This is safe, but it could leak +/// # 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. /// @@ -490,20 +338,9 @@ pub unsafe fn read_unaligned<T>(src: *const T) -> T { /// location pointed to by `dst`. /// /// This is appropriate for initializing uninitialized memory, or overwriting -/// memory that has previously been [`read`] from. +/// memory that has previously been `read` from. /// -/// [`read`]: ./fn.read.html -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must point to valid memory. -/// -/// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the -/// case. -/// -/// [`write_unaligned`]: ./fn.write_unaligned.html +/// The pointer must be aligned; use `write_unaligned` if that is not the case. /// /// # Examples /// @@ -519,30 +356,6 @@ pub unsafe fn read_unaligned<T>(src: *const T) -> T { /// assert_eq!(std::ptr::read(y), 12); /// } /// ``` -/// -/// Manually implement [`mem::swap`]: -/// -/// ``` -/// use std::ptr; -/// -/// fn swap<T>(a: &mut T, b: &mut T) { -/// unsafe { -/// let tmp = ptr::read(a); -/// ptr::copy_nonoverlapping(b, a, 1); -/// ptr::write(b, tmp); -/// } -/// } -/// -/// let mut foo = "foo".to_owned(); -/// let mut bar = "bar".to_owned(); -/// -/// swap(&mut foo, &mut bar); -/// -/// assert_eq!(foo, "bar"); -/// assert_eq!(bar, "foo"); -/// ``` -/// -/// [`mem::swap`]: ../mem/fn.swap.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn write<T>(dst: *mut T, src: T) { @@ -552,58 +365,36 @@ pub unsafe fn write<T>(dst: *mut T, src: T) { /// Overwrites a memory location with the given value without reading or /// dropping the old value. /// -/// Unlike [`write`], the pointer may be unaligned. +/// Unlike `write`, the pointer may be unaligned. +/// +/// # Safety +/// +/// This operation is marked unsafe because it accepts a raw pointer. /// -/// `write_unaligned` 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. +/// 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. /// /// Additionally, it does not drop `src`. Semantically, `src` is moved into the /// location pointed to by `dst`. /// /// This is appropriate for initializing uninitialized memory, or overwriting -/// memory that has previously been read with [`read_unaligned`]. -/// -/// [`write`]: ./fn.write.html -/// [`read_unaligned`]: ./fn.read_unaligned.html -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must point to valid memory. +/// memory that has previously been `read` from. /// /// # Examples /// -/// Access fields in a packed struct: +/// Basic usage: /// /// ``` -/// use std::{mem, ptr}; -/// -/// #[repr(packed, C)] -/// #[derive(Default)] -/// struct Packed { -/// _padding: u8, -/// unaligned: u32, -/// } -/// -/// let v = 0x01020304; -/// let mut x: Packed = unsafe { mem::zeroed() }; +/// let mut x = 0; +/// let y = &mut x as *mut i32; +/// let z = 12; /// /// unsafe { -/// // Take a reference to a 32-bit integer which is not aligned. -/// let unaligned = &mut x.unaligned; -/// -/// // Dereferencing normally will emit an unaligned store instruction, -/// // causing undefined behavior. -/// // *unaligned = v; // ERROR -/// -/// // Instead, use `write_unaligned` to write improperly aligned values. -/// ptr::write_unaligned(unaligned, v); +/// std::ptr::write_unaligned(y, z); +/// assert_eq!(std::ptr::read_unaligned(y), 12); /// } -/// -/// // Accessing unaligned values directly is safe. -/// assert!(x.unaligned == v); +/// ``` #[inline] #[stable(feature = "ptr_unaligned", since = "1.17.0")] pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) { @@ -620,11 +411,6 @@ pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) { /// to not be elided or reordered by the compiler across other volatile /// operations. /// -/// Memory read with `read_volatile` should almost always be written to using -/// [`write_volatile`]. -/// -/// [`write_volatile`]: ./fn.write_volatile.html -/// /// # Notes /// /// Rust does not currently have a rigorously and formally defined memory model, @@ -641,19 +427,12 @@ pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) { /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must point to valid, initialized memory. -/// -/// * `src` must be properly aligned. -/// -/// Like [`read`], `read_volatile` creates a bitwise copy of the pointed-to -/// object, regardless of whether `T` is [`Copy`]. Using both values can cause -/// undefined behavior. However, storing non-[`Copy`] data in I/O memory is -/// almost certainly incorrect. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read`]: ./fn.read.html +/// 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`, +/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use +/// because it will attempt to drop the value previously at `*src`. /// /// # Examples /// @@ -680,18 +459,6 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T { /// to not be elided or reordered by the compiler across other volatile /// operations. /// -/// Memory written with `write_volatile` should almost always be read from using -/// [`read_volatile`]. -/// -/// `write_volatile` 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. -/// -/// Additionally, it does not drop `src`. Semantically, `src` is moved into the -/// location pointed to by `dst`. -/// -/// [`read_volatile`]: ./fn.read_volatile.html -/// /// # Notes /// /// Rust does not currently have a rigorously and formally defined memory model, @@ -708,11 +475,14 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T { /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: +/// This operation is marked unsafe because it accepts a raw pointer. /// -/// * `dst` must point to valid memory. +/// 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. /// -/// * `dst` must be properly aligned. +/// This is appropriate for initializing uninitialized memory, or overwriting +/// memory that has previously been `read` from. /// /// # Examples /// @@ -1433,15 +1203,22 @@ impl<T: ?Sized> *const T { copy_nonoverlapping(self, dest, count) } - /// Computes the byte offset that needs to be applied in order to - /// make the pointer aligned to `align`. + /// Computes the offset that needs to be applied to the pointer in order to make it aligned to + /// `align`. + /// /// If it is not possible to align the pointer, the implementation returns /// `usize::max_value()`. /// - /// There are no guarantees whatsover that offsetting the pointer will not - /// overflow or go beyond the allocation that the pointer points into. - /// It is up to the caller to ensure that the returned offset is correct - /// in all terms other than alignment. + /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be + /// used with the `offset` or `offset_to` methods. + /// + /// There are no guarantees whatsover that offsetting the pointer will not overflow or go + /// beyond the allocation that the pointer points into. It is up to the caller to ensure that + /// the returned offset is correct in all terms other than alignment. + /// + /// # Panics + /// + /// The function panics if `align` is not a power-of-two. /// /// # Examples /// @@ -1465,13 +1242,30 @@ impl<T: ?Sized> *const T { /// # } } /// ``` #[unstable(feature = "align_offset", issue = "44488")] - pub fn align_offset(self, align: usize) -> usize { + #[cfg(not(stage0))] + pub fn align_offset(self, align: usize) -> usize where T: Sized { + if !align.is_power_of_two() { + panic!("align_offset: align is not a power-of-two"); + } unsafe { - intrinsics::align_offset(self as *const _, align) + align_offset(self, align) + } + } + + /// definitely docs. + #[unstable(feature = "align_offset", issue = "44488")] + #[cfg(stage0)] + pub fn align_offset(self, align: usize) -> usize where T: Sized { + if !align.is_power_of_two() { + panic!("align_offset: align is not a power-of-two"); + } + unsafe { + intrinsics::align_offset(self as *const (), align) } } } + #[lang = "mut_ptr"] impl<T: ?Sized> *mut T { /// Returns `true` if the pointer is null. @@ -1804,44 +1598,6 @@ impl<T: ?Sized> *mut T { (self as *const T).wrapping_offset_from(origin) } - /// Computes the byte offset that needs to be applied in order to - /// make the pointer aligned to `align`. - /// If it is not possible to align the pointer, the implementation returns - /// `usize::max_value()`. - /// - /// There are no guarantees whatsover that offsetting the pointer will not - /// overflow or go beyond the allocation that the pointer points into. - /// It is up to the caller to ensure that the returned offset is correct - /// in all terms other than alignment. - /// - /// # Examples - /// - /// Accessing adjacent `u8` as `u16` - /// - /// ``` - /// # #![feature(align_offset)] - /// # fn foo(n: usize) { - /// # use std::mem::align_of; - /// # unsafe { - /// let x = [5u8, 6u8, 7u8, 8u8, 9u8]; - /// let ptr = &x[n] as *const u8; - /// let offset = ptr.align_offset(align_of::<u16>()); - /// if offset < x.len() - n - 1 { - /// let u16_ptr = ptr.offset(offset as isize) as *const u16; - /// assert_ne!(*u16_ptr, 500); - /// } else { - /// // while the pointer can be aligned via `offset`, it would point - /// // outside the allocation - /// } - /// # } } - /// ``` - #[unstable(feature = "align_offset", issue = "44488")] - pub fn align_offset(self, align: usize) -> usize { - unsafe { - intrinsics::align_offset(self as *const _, align) - } - } - /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). /// /// `count` is in units of T; e.g. a `count` of 3 represents a pointer @@ -2511,8 +2267,200 @@ impl<T: ?Sized> *mut T { { swap(self, with) } + + /// Computes the offset that needs to be applied to the pointer in order to make it aligned to + /// `align`. + /// + /// If it is not possible to align the pointer, the implementation returns + /// `usize::max_value()`. + /// + /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be + /// used with the `offset` or `offset_to` methods. + /// + /// There are no guarantees whatsover that offsetting the pointer will not overflow or go + /// beyond the allocation that the pointer points into. It is up to the caller to ensure that + /// the returned offset is correct in all terms other than alignment. + /// + /// # Panics + /// + /// The function panics if `align` is not a power-of-two. + /// + /// # Examples + /// + /// Accessing adjacent `u8` as `u16` + /// + /// ``` + /// # #![feature(align_offset)] + /// # fn foo(n: usize) { + /// # use std::mem::align_of; + /// # unsafe { + /// let x = [5u8, 6u8, 7u8, 8u8, 9u8]; + /// let ptr = &x[n] as *const u8; + /// let offset = ptr.align_offset(align_of::<u16>()); + /// if offset < x.len() - n - 1 { + /// let u16_ptr = ptr.offset(offset as isize) as *const u16; + /// assert_ne!(*u16_ptr, 500); + /// } else { + /// // while the pointer can be aligned via `offset`, it would point + /// // outside the allocation + /// } + /// # } } + /// ``` + #[unstable(feature = "align_offset", issue = "44488")] + #[cfg(not(stage0))] + pub fn align_offset(self, align: usize) -> usize where T: Sized { + if !align.is_power_of_two() { + panic!("align_offset: align is not a power-of-two"); + } + unsafe { + align_offset(self, align) + } + } + + /// definitely docs. + #[unstable(feature = "align_offset", issue = "44488")] + #[cfg(stage0)] + pub fn align_offset(self, align: usize) -> usize where T: Sized { + if !align.is_power_of_two() { + panic!("align_offset: align is not a power-of-two"); + } + unsafe { + intrinsics::align_offset(self as *const (), align) + } + } +} + +/// Align pointer `p`. +/// +/// Calculate offset (in terms of elements of `stride` stride) that has to be applied +/// to pointer `p` so that pointer `p` would get aligned to `a`. +/// +/// Note: This implementation has been carefully tailored to not panic. It is UB for this to panic. +/// The only real change that can be made here is change of `INV_TABLE_MOD_16` and associated +/// constants. +/// +/// If we ever decide to make it possible to call the intrinsic with `a` that is not a +/// power-of-two, it will probably be more prudent to just change to a naive implementation rather +/// than trying to adapt this to accomodate that change. +/// +/// Any questions go to @nagisa. +#[lang="align_offset"] +#[cfg(not(stage0))] +pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize { + /// Calculate multiplicative modular inverse of `x` modulo `m`. + /// + /// This implementation is tailored for align_offset and has following preconditions: + /// + /// * `m` is a power-of-two; + /// * `x < m`; (if `x ≥ m`, pass in `x % m` instead) + /// + /// Implementation of this function shall not panic. Ever. + #[inline] + fn mod_inv(x: usize, m: usize) -> usize { + /// Multiplicative modular inverse table modulo 2⁴ = 16. + /// + /// Note, that this table does not contain values where inverse does not exist (i.e. for + /// `0⁻¹ mod 16`, `2⁻¹ mod 16`, etc.) + const INV_TABLE_MOD_16: [usize; 8] = [1, 11, 13, 7, 9, 3, 5, 15]; + /// Modulo for which the `INV_TABLE_MOD_16` is intended. + const INV_TABLE_MOD: usize = 16; + /// INV_TABLE_MOD² + const INV_TABLE_MOD_SQUARED: usize = INV_TABLE_MOD * INV_TABLE_MOD; + + let table_inverse = INV_TABLE_MOD_16[(x & (INV_TABLE_MOD - 1)) >> 1]; + if m <= INV_TABLE_MOD { + return table_inverse & (m - 1); + } else { + // We iterate "up" using the following formula: + // + // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$ + // + // until 2²ⁿ ≥ m. Then we can reduce to our desired `m` by taking the result `mod m`. + let mut inverse = table_inverse; + let mut going_mod = INV_TABLE_MOD_SQUARED; + loop { + // y = y * (2 - xy) mod n + // + // Note, that we use wrapping operations here intentionally – the original formula + // uses e.g. subtraction `mod n`. It is entirely fine to do them `mod + // usize::max_value()` instead, because we take the result `mod n` at the end + // anyway. + inverse = inverse.wrapping_mul( + 2usize.wrapping_sub(x.wrapping_mul(inverse)) + ) & (going_mod - 1); + if going_mod > m { + return inverse & (m - 1); + } + going_mod = going_mod.wrapping_mul(going_mod); + } + } + } + + let stride = ::mem::size_of::<T>(); + let a_minus_one = a.wrapping_sub(1); + let pmoda = p as usize & a_minus_one; + + if pmoda == 0 { + // Already aligned. Yay! + return 0; + } + + if stride <= 1 { + return if stride == 0 { + // If the pointer is not aligned, and the element is zero-sized, then no amount of + // elements will ever align the pointer. + !0 + } else { + a.wrapping_sub(pmoda) + }; + } + + let smoda = stride & a_minus_one; + // a is power-of-two so cannot be 0. stride = 0 is handled above. + let gcdpow = intrinsics::cttz_nonzero(stride).min(intrinsics::cttz_nonzero(a)); + let gcd = 1usize << gcdpow; + + if gcd == 1 { + // This branch solves for the variable $o$ in following linear congruence equation: + // + // ⎰ p + o ≡ 0 (mod a) # $p + o$ must be aligned to specified alignment $a$ + // ⎱ o ≡ 0 (mod s) # offset $o$ must be a multiple of stride $s$ + // + // where + // + // * a, s are co-prime + // + // This gives us the formula below: + // + // o = (a - (p mod a)) * (s⁻¹ mod a) * s + // + // The first term is “the relative alignment of p to a”, the second term is “how does + // incrementing p by one s change the relative alignment of p”, the third term is + // translating change in units of s to a byte count. + // + // Furthermore, the result produced by this solution is not “minimal”, so it is necessary + // to take the result $o mod lcm(s, a)$. Since $s$ and $a$ are co-prime (i.e. $gcd(s, a) = + // 1$) and $lcm(s, a) = s * a / gcd(s, a)$, we can replace $lcm(s, a)$ with just a $s * a$. + // + // (Author note: we decided later on to express the offset in "elements" rather than bytes, + // which drops the multiplication by `s` on both sides of the modulo.) + return intrinsics::unchecked_rem(a.wrapping_sub(pmoda).wrapping_mul(mod_inv(smoda, a)), a); + } + + if p as usize & (gcd - 1) == 0 { + // This can be aligned, but `a` and `stride` are not co-prime, so a somewhat adapted + // formula is used. + let j = a.wrapping_sub(pmoda) >> gcdpow; + let k = smoda >> gcdpow; + return intrinsics::unchecked_rem(j.wrapping_mul(mod_inv(k, a)), a >> gcdpow); + } + + // Cannot be aligned at all. + return usize::max_value(); } + + // Equality for pointers #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> PartialEq for *const T { diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 93ebc23ac0b..3b19a401859 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -68,181 +68,6 @@ struct Repr<T> { // Extension traits // -public_in_stage0! { -{ -/// Extension methods for slices. -#[unstable(feature = "core_slice_ext", - reason = "stable interface provided by `impl [T]` in later crates", - issue = "32110")] -#[allow(missing_docs)] // documented elsewhere -} -trait SliceExt { - type Item; - - #[stable(feature = "core", since = "1.6.0")] - fn split_at(&self, mid: usize) -> (&[Self::Item], &[Self::Item]); - - #[stable(feature = "core", since = "1.6.0")] - fn iter(&self) -> Iter<Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split<P>(&self, pred: P) -> Split<Self::Item, P> - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "slice_rsplit", since = "1.27.0")] - fn rsplit<P>(&self, pred: P) -> RSplit<Self::Item, P> - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn splitn<P>(&self, n: usize, pred: P) -> SplitN<Self::Item, P> - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn rsplitn<P>(&self, n: usize, pred: P) -> RSplitN<Self::Item, P> - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn windows(&self, size: usize) -> Windows<Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn chunks(&self, size: usize) -> Chunks<Self::Item>; - - #[unstable(feature = "exact_chunks", issue = "47115")] - fn exact_chunks(&self, size: usize) -> ExactChunks<Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn get<I>(&self, index: I) -> Option<&I::Output> - where I: SliceIndex<Self>; - #[stable(feature = "core", since = "1.6.0")] - fn first(&self) -> Option<&Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_first(&self) -> Option<(&Self::Item, &[Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_last(&self) -> Option<(&Self::Item, &[Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn last(&self) -> Option<&Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - unsafe fn get_unchecked<I>(&self, index: I) -> &I::Output - where I: SliceIndex<Self>; - #[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; - - #[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> - where F: FnMut(&'a Self::Item) -> B, - B: Ord; - - #[stable(feature = "core", since = "1.6.0")] - fn len(&self) -> usize; - - #[stable(feature = "core", since = "1.6.0")] - fn is_empty(&self) -> bool { self.len() == 0 } - - #[stable(feature = "core", since = "1.6.0")] - fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output> - where I: SliceIndex<Self>; - #[stable(feature = "core", since = "1.6.0")] - fn iter_mut(&mut self) -> IterMut<Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn first_mut(&mut self) -> Option<&mut Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_first_mut(&mut self) -> Option<(&mut Self::Item, &mut [Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_last_mut(&mut self) -> Option<(&mut Self::Item, &mut [Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn last_mut(&mut self) -> Option<&mut Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_mut<P>(&mut self, pred: P) -> SplitMut<Self::Item, P> - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "slice_rsplit", since = "1.27.0")] - fn rsplit_mut<P>(&mut self, pred: P) -> RSplitMut<Self::Item, P> - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn splitn_mut<P>(&mut self, n: usize, pred: P) -> SplitNMut<Self::Item, P> - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn rsplitn_mut<P>(&mut self, n: usize, pred: P) -> RSplitNMut<Self::Item, P> - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<Self::Item>; - - #[unstable(feature = "exact_chunks", issue = "47115")] - fn exact_chunks_mut(&mut self, size: usize) -> ExactChunksMut<Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn swap(&mut self, a: usize, b: usize); - - #[stable(feature = "core", since = "1.6.0")] - fn split_at_mut(&mut self, mid: usize) -> (&mut [Self::Item], &mut [Self::Item]); - - #[stable(feature = "core", since = "1.6.0")] - fn reverse(&mut self); - - #[stable(feature = "core", since = "1.6.0")] - unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output - where I: SliceIndex<Self>; - #[stable(feature = "core", since = "1.6.0")] - fn as_mut_ptr(&mut self) -> *mut Self::Item; - - #[stable(feature = "core", since = "1.6.0")] - fn contains(&self, x: &Self::Item) -> bool where Self::Item: PartialEq; - - #[stable(feature = "core", since = "1.6.0")] - fn starts_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq; - - #[stable(feature = "core", since = "1.6.0")] - fn ends_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq; - - #[stable(feature = "slice_rotate", since = "1.26.0")] - fn rotate_left(&mut self, mid: usize); - - #[stable(feature = "slice_rotate", since = "1.26.0")] - fn rotate_right(&mut self, k: usize); - - #[stable(feature = "clone_from_slice", since = "1.7.0")] - fn clone_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Clone; - - #[stable(feature = "copy_from_slice", since = "1.9.0")] - fn copy_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Copy; - - #[stable(feature = "swap_with_slice", since = "1.27.0")] - fn swap_with_slice(&mut self, src: &mut [Self::Item]); - - #[stable(feature = "sort_unstable", since = "1.20.0")] - fn sort_unstable(&mut self) - where Self::Item: Ord; - - #[stable(feature = "sort_unstable", since = "1.20.0")] - fn sort_unstable_by<F>(&mut self, compare: F) - where F: FnMut(&Self::Item, &Self::Item) -> Ordering; - - #[stable(feature = "sort_unstable", since = "1.20.0")] - fn sort_unstable_by_key<B, F>(&mut self, f: F) - where F: FnMut(&Self::Item) -> B, - B: Ord; -}} - // Use macros to be generic over const/mut macro_rules! slice_offset { ($ptr:expr, $by:expr) => {{ @@ -281,488 +106,9 @@ macro_rules! make_ref_mut { }}; } -#[unstable(feature = "core_slice_ext", - reason = "stable interface provided by `impl [T]` in later crates", - issue = "32110")] -impl<T> SliceExt for [T] { - type Item = T; - - #[inline] - fn split_at(&self, mid: usize) -> (&[T], &[T]) { - (&self[..mid], &self[mid..]) - } - - #[inline] - fn iter(&self) -> Iter<T> { - unsafe { - let p = if mem::size_of::<T>() == 0 { - 1 as *const _ - } else { - let p = self.as_ptr(); - assume(!p.is_null()); - p - }; - - Iter { - ptr: p, - end: slice_offset!(p, self.len() as isize), - _marker: marker::PhantomData - } - } - } - - #[inline] - fn split<P>(&self, pred: P) -> Split<T, P> - where P: FnMut(&T) -> bool - { - Split { - v: self, - pred, - finished: false - } - } - - #[inline] - fn rsplit<P>(&self, pred: P) -> RSplit<T, P> - where P: FnMut(&T) -> bool - { - RSplit { inner: self.split(pred) } - } - - #[inline] - fn splitn<P>(&self, n: usize, pred: P) -> SplitN<T, P> - where P: FnMut(&T) -> bool - { - SplitN { - inner: GenericSplitN { - iter: self.split(pred), - count: n - } - } - } - - #[inline] - fn rsplitn<P>(&self, n: usize, pred: P) -> RSplitN<T, P> - where P: FnMut(&T) -> bool - { - RSplitN { - inner: GenericSplitN { - iter: self.rsplit(pred), - count: n - } - } - } - - #[inline] - fn windows(&self, size: usize) -> Windows<T> { - assert!(size != 0); - Windows { v: self, size: size } - } - - #[inline] - fn chunks(&self, chunk_size: usize) -> Chunks<T> { - assert!(chunk_size != 0); - Chunks { v: self, chunk_size: chunk_size } - } - - #[inline] - fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let len = self.len() - rem; - ExactChunks { v: &self[..len], chunk_size: chunk_size} - } - - #[inline] - fn get<I>(&self, index: I) -> Option<&I::Output> - where I: SliceIndex<[T]> - { - index.get(self) - } - - #[inline] - fn first(&self) -> Option<&T> { - if self.is_empty() { None } else { Some(&self[0]) } - } - - #[inline] - fn split_first(&self) -> Option<(&T, &[T])> { - if self.is_empty() { None } else { Some((&self[0], &self[1..])) } - } - - #[inline] - fn split_last(&self) -> Option<(&T, &[T])> { - let len = self.len(); - if len == 0 { None } else { Some((&self[len - 1], &self[..(len - 1)])) } - } - - #[inline] - fn last(&self) -> Option<&T> { - if self.is_empty() { None } else { Some(&self[self.len() - 1]) } - } - - #[inline] - unsafe fn get_unchecked<I>(&self, index: I) -> &I::Output - where I: SliceIndex<[T]> - { - index.get_unchecked(self) - } - - #[inline] - fn as_ptr(&self) -> *const T { - self as *const [T] as *const T - } - - fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize> - where F: FnMut(&'a T) -> Ordering - { - let s = self; - let mut size = s.len(); - if size == 0 { - return Err(0); - } - let mut base = 0usize; - while size > 1 { - let half = size / 2; - let mid = base + half; - // mid is always in [0, size), that means mid is >= 0 and < size. - // mid >= 0: by definition - // mid < size: mid = size / 2 + size / 4 + size / 8 ... - let cmp = f(unsafe { s.get_unchecked(mid) }); - base = if cmp == Greater { base } else { mid }; - size -= half; - } - // base is always in [0, size) because base <= mid. - let cmp = f(unsafe { s.get_unchecked(base) }); - if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } - } - - #[inline] - fn len(&self) -> usize { - unsafe { - mem::transmute::<&[T], Repr<T>>(self).len - } - } - - #[inline] - fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output> - where I: SliceIndex<[T]> - { - index.get_mut(self) - } - - #[inline] - fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { - let len = self.len(); - let ptr = self.as_mut_ptr(); - - unsafe { - assert!(mid <= len); - - (from_raw_parts_mut(ptr, mid), - from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) - } - } - - #[inline] - fn iter_mut(&mut self) -> IterMut<T> { - unsafe { - let p = if mem::size_of::<T>() == 0 { - 1 as *mut _ - } else { - let p = self.as_mut_ptr(); - assume(!p.is_null()); - p - }; - - IterMut { - ptr: p, - end: slice_offset!(p, self.len() as isize), - _marker: marker::PhantomData - } - } - } - - #[inline] - fn last_mut(&mut self) -> Option<&mut T> { - let len = self.len(); - if len == 0 { return None; } - Some(&mut self[len - 1]) - } - - #[inline] - fn first_mut(&mut self) -> Option<&mut T> { - if self.is_empty() { None } else { Some(&mut self[0]) } - } - - #[inline] - fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { - if self.is_empty() { None } else { - let split = self.split_at_mut(1); - Some((&mut split.0[0], split.1)) - } - } - - #[inline] - fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { - let len = self.len(); - if len == 0 { None } else { - let split = self.split_at_mut(len - 1); - Some((&mut split.1[0], split.0)) - } - } - - #[inline] - fn split_mut<P>(&mut self, pred: P) -> SplitMut<T, P> - where P: FnMut(&T) -> bool - { - SplitMut { v: self, pred: pred, finished: false } - } - - #[inline] - fn rsplit_mut<P>(&mut self, pred: P) -> RSplitMut<T, P> - where P: FnMut(&T) -> bool - { - RSplitMut { inner: self.split_mut(pred) } - } - - #[inline] - fn splitn_mut<P>(&mut self, n: usize, pred: P) -> SplitNMut<T, P> - where P: FnMut(&T) -> bool - { - SplitNMut { - inner: GenericSplitN { - iter: self.split_mut(pred), - count: n - } - } - } - - #[inline] - fn rsplitn_mut<P>(&mut self, n: usize, pred: P) -> RSplitNMut<T, P> where - P: FnMut(&T) -> bool, - { - RSplitNMut { - inner: GenericSplitN { - iter: self.rsplit_mut(pred), - count: n - } - } - } - - #[inline] - fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<T> { - assert!(chunk_size != 0); - ChunksMut { v: self, chunk_size: chunk_size } - } - - #[inline] - fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<T> { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let len = self.len() - rem; - ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size} - } - - #[inline] - fn swap(&mut self, a: usize, b: usize) { - unsafe { - // Can't take two mutable loans from one vector, so instead just cast - // them to their raw pointers to do the swap - let pa: *mut T = &mut self[a]; - let pb: *mut T = &mut self[b]; - ptr::swap(pa, pb); - } - } - - fn reverse(&mut self) { - let mut i: usize = 0; - let ln = self.len(); - - // For very small types, all the individual reads in the normal - // path perform poorly. We can do better, given efficient unaligned - // load/store, by loading a larger chunk and reversing a register. - - // Ideally LLVM would do this for us, as it knows better than we do - // whether unaligned reads are efficient (since that changes between - // different ARM versions, for example) and what the best chunk size - // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls - // the loop, so we need to do this ourselves. (Hypothesis: reverse - // is troublesome because the sides can be aligned differently -- - // will be, when the length is odd -- so there's no way of emitting - // pre- and postludes to use fully-aligned SIMD in the middle.) - - let fast_unaligned = - cfg!(any(target_arch = "x86", target_arch = "x86_64")); - - if fast_unaligned && mem::size_of::<T>() == 1 { - // Use the llvm.bswap intrinsic to reverse u8s in a usize - let chunk = mem::size_of::<usize>(); - while i + chunk - 1 < ln / 2 { - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); - let va = ptr::read_unaligned(pa as *mut usize); - let vb = ptr::read_unaligned(pb as *mut usize); - ptr::write_unaligned(pa as *mut usize, vb.swap_bytes()); - ptr::write_unaligned(pb as *mut usize, va.swap_bytes()); - } - i += chunk; - } - } - - if fast_unaligned && mem::size_of::<T>() == 2 { - // Use rotate-by-16 to reverse u16s in a u32 - let chunk = mem::size_of::<u32>() / 2; - while i + chunk - 1 < ln / 2 { - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); - let va = ptr::read_unaligned(pa as *mut u32); - let vb = ptr::read_unaligned(pb as *mut u32); - ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16)); - ptr::write_unaligned(pb as *mut u32, va.rotate_left(16)); - } - i += chunk; - } - } - - while i < ln / 2 { - // Unsafe swap to avoid the bounds check in safe swap. - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - 1); - ptr::swap(pa, pb); - } - i += 1; - } - } - - #[inline] - unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output - where I: SliceIndex<[T]> - { - index.get_unchecked_mut(self) - } - - #[inline] - fn as_mut_ptr(&mut self) -> *mut T { - self as *mut [T] as *mut T - } - - #[inline] - fn contains(&self, x: &T) -> bool where T: PartialEq { - x.slice_contains(self) - } - - #[inline] - fn starts_with(&self, needle: &[T]) -> bool where T: PartialEq { - let n = needle.len(); - self.len() >= n && needle == &self[..n] - } - - #[inline] - fn ends_with(&self, needle: &[T]) -> bool where T: PartialEq { - let (m, n) = (self.len(), needle.len()); - 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 rotate_left(&mut self, mid: usize) { - assert!(mid <= self.len()); - let k = self.len() - mid; - - unsafe { - let p = self.as_mut_ptr(); - rotate::ptr_rotate(mid, p.offset(mid as isize), k); - } - } - - fn rotate_right(&mut self, k: usize) { - assert!(k <= self.len()); - let mid = self.len() - k; - - unsafe { - let p = self.as_mut_ptr(); - rotate::ptr_rotate(mid, p.offset(mid as isize), k); - } - } - - #[inline] - fn clone_from_slice(&mut self, src: &[T]) where T: Clone { - assert!(self.len() == src.len(), - "destination and source slices have different lengths"); - // NOTE: We need to explicitly slice them to the same length - // for bounds checking to be elided, and the optimizer will - // generate memcpy for simple cases (for example T = u8). - let len = self.len(); - let src = &src[..len]; - for i in 0..len { - self[i].clone_from(&src[i]); - } - } - - #[inline] - fn copy_from_slice(&mut self, src: &[T]) where T: Copy { - assert!(self.len() == src.len(), - "destination and source slices have different lengths"); - unsafe { - ptr::copy_nonoverlapping( - src.as_ptr(), self.as_mut_ptr(), self.len()); - } - } - - #[inline] - fn swap_with_slice(&mut self, src: &mut [T]) { - assert!(self.len() == src.len(), - "destination and source slices have different lengths"); - unsafe { - ptr::swap_nonoverlapping( - self.as_mut_ptr(), src.as_mut_ptr(), self.len()); - } - } - - #[inline] - fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result<usize, usize> - where F: FnMut(&'a Self::Item) -> B, - B: Ord - { - self.binary_search_by(|k| f(k).cmp(b)) - } - - #[inline] - fn sort_unstable(&mut self) - where Self::Item: Ord - { - sort::quicksort(self, |a, b| a.lt(b)); - } - - #[inline] - fn sort_unstable_by<F>(&mut self, mut compare: F) - where F: FnMut(&Self::Item, &Self::Item) -> Ordering - { - sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); - } - - #[inline] - fn sort_unstable_by_key<B, F>(&mut self, mut f: F) - where F: FnMut(&Self::Item) -> B, - B: Ord - { - sort::quicksort(self, |a, b| f(a).lt(&f(b))); - } -} - -// FIXME: remove (inline) this macro and the SliceExt trait -// when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] -#[unstable(feature = "core_slice_ext", issue = "32110")] -macro_rules! slice_core_methods { () => { +#[lang = "slice"] +#[cfg(not(test))] +impl<T> [T] { /// Returns the number of elements in the slice. /// /// # Examples @@ -774,7 +120,9 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn len(&self) -> usize { - SliceExt::len(self) + unsafe { + mem::transmute::<&[T], Repr<T>>(self).len + } } /// Returns `true` if the slice has a length of 0. @@ -788,7 +136,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_empty(&self) -> bool { - SliceExt::is_empty(self) + self.len() == 0 } /// Returns the first element of the slice, or `None` if it is empty. @@ -805,7 +153,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn first(&self) -> Option<&T> { - SliceExt::first(self) + if self.is_empty() { None } else { Some(&self[0]) } } /// Returns a mutable pointer to the first element of the slice, or `None` if it is empty. @@ -823,7 +171,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn first_mut(&mut self) -> Option<&mut T> { - SliceExt::first_mut(self) + if self.is_empty() { None } else { Some(&mut self[0]) } } /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. @@ -841,7 +189,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_first(&self) -> Option<(&T, &[T])> { - SliceExt::split_first(self) + if self.is_empty() { None } else { Some((&self[0], &self[1..])) } } /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. @@ -861,7 +209,10 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { - SliceExt::split_first_mut(self) + if self.is_empty() { None } else { + let split = self.split_at_mut(1); + Some((&mut split.0[0], split.1)) + } } /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. @@ -879,7 +230,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_last(&self) -> Option<(&T, &[T])> { - SliceExt::split_last(self) + let len = self.len(); + if len == 0 { None } else { Some((&self[len - 1], &self[..(len - 1)])) } } /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. @@ -899,7 +251,12 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { - SliceExt::split_last_mut(self) + let len = self.len(); + if len == 0 { None } else { + let split = self.split_at_mut(len - 1); + Some((&mut split.1[0], split.0)) + } + } /// Returns the last element of the slice, or `None` if it is empty. @@ -916,7 +273,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn last(&self) -> Option<&T> { - SliceExt::last(self) + if self.is_empty() { None } else { Some(&self[self.len() - 1]) } } /// Returns a mutable pointer to the last item in the slice. @@ -934,7 +291,9 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn last_mut(&mut self) -> Option<&mut T> { - SliceExt::last_mut(self) + let len = self.len(); + if len == 0 { return None; } + Some(&mut self[len - 1]) } /// Returns a reference to an element or subslice depending on the type of @@ -959,7 +318,7 @@ macro_rules! slice_core_methods { () => { pub fn get<I>(&self, index: I) -> Option<&I::Output> where I: SliceIndex<Self> { - SliceExt::get(self, index) + index.get(self) } /// Returns a mutable reference to an element or subslice depending on the @@ -982,7 +341,7 @@ macro_rules! slice_core_methods { () => { pub fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output> where I: SliceIndex<Self> { - SliceExt::get_mut(self, index) + index.get_mut(self) } /// Returns a reference to an element or subslice, without doing bounds @@ -1007,7 +366,7 @@ macro_rules! slice_core_methods { () => { pub unsafe fn get_unchecked<I>(&self, index: I) -> &I::Output where I: SliceIndex<Self> { - SliceExt::get_unchecked(self, index) + index.get_unchecked(self) } /// Returns a mutable reference to an element or subslice, without doing @@ -1034,7 +393,7 @@ macro_rules! slice_core_methods { () => { pub unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output where I: SliceIndex<Self> { - SliceExt::get_unchecked_mut(self, index) + index.get_unchecked_mut(self) } /// Returns a raw pointer to the slice's buffer. @@ -1060,7 +419,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn as_ptr(&self) -> *const T { - SliceExt::as_ptr(self) + self as *const [T] as *const T } /// Returns an unsafe mutable pointer to the slice's buffer. @@ -1087,7 +446,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn as_mut_ptr(&mut self) -> *mut T { - SliceExt::as_mut_ptr(self) + self as *mut [T] as *mut T } /// Swaps two elements in the slice. @@ -1111,7 +470,13 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn swap(&mut self, a: usize, b: usize) { - SliceExt::swap(self, a, b) + unsafe { + // Can't take two mutable loans from one vector, so instead just cast + // them to their raw pointers to do the swap + let pa: *mut T = &mut self[a]; + let pb: *mut T = &mut self[b]; + ptr::swap(pa, pb); + } } /// Reverses the order of elements in the slice, in place. @@ -1126,7 +491,66 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn reverse(&mut self) { - SliceExt::reverse(self) + let mut i: usize = 0; + let ln = self.len(); + + // For very small types, all the individual reads in the normal + // path perform poorly. We can do better, given efficient unaligned + // load/store, by loading a larger chunk and reversing a register. + + // Ideally LLVM would do this for us, as it knows better than we do + // whether unaligned reads are efficient (since that changes between + // different ARM versions, for example) and what the best chunk size + // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls + // the loop, so we need to do this ourselves. (Hypothesis: reverse + // is troublesome because the sides can be aligned differently -- + // will be, when the length is odd -- so there's no way of emitting + // pre- and postludes to use fully-aligned SIMD in the middle.) + + let fast_unaligned = + cfg!(any(target_arch = "x86", target_arch = "x86_64")); + + if fast_unaligned && mem::size_of::<T>() == 1 { + // Use the llvm.bswap intrinsic to reverse u8s in a usize + let chunk = mem::size_of::<usize>(); + while i + chunk - 1 < ln / 2 { + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); + let va = ptr::read_unaligned(pa as *mut usize); + let vb = ptr::read_unaligned(pb as *mut usize); + ptr::write_unaligned(pa as *mut usize, vb.swap_bytes()); + ptr::write_unaligned(pb as *mut usize, va.swap_bytes()); + } + i += chunk; + } + } + + if fast_unaligned && mem::size_of::<T>() == 2 { + // Use rotate-by-16 to reverse u16s in a u32 + let chunk = mem::size_of::<u32>() / 2; + while i + chunk - 1 < ln / 2 { + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); + let va = ptr::read_unaligned(pa as *mut u32); + let vb = ptr::read_unaligned(pb as *mut u32); + ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16)); + ptr::write_unaligned(pb as *mut u32, va.rotate_left(16)); + } + i += chunk; + } + } + + while i < ln / 2 { + // Unsafe swap to avoid the bounds check in safe swap. + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - 1); + ptr::swap(pa, pb); + } + i += 1; + } } /// Returns an iterator over the slice. @@ -1145,7 +569,21 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn iter(&self) -> Iter<T> { - SliceExt::iter(self) + unsafe { + let p = if mem::size_of::<T>() == 0 { + 1 as *const _ + } else { + let p = self.as_ptr(); + assume(!p.is_null()); + p + }; + + Iter { + ptr: p, + end: slice_offset!(p, self.len() as isize), + _marker: marker::PhantomData + } + } } /// Returns an iterator that allows modifying each value. @@ -1162,7 +600,21 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn iter_mut(&mut self) -> IterMut<T> { - SliceExt::iter_mut(self) + unsafe { + let p = if mem::size_of::<T>() == 0 { + 1 as *mut _ + } else { + let p = self.as_mut_ptr(); + assume(!p.is_null()); + p + }; + + IterMut { + ptr: p, + end: slice_offset!(p, self.len() as isize), + _marker: marker::PhantomData + } + } } /// Returns an iterator over all contiguous windows of length @@ -1194,7 +646,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn windows(&self, size: usize) -> Windows<T> { - SliceExt::windows(self, size) + assert!(size != 0); + Windows { v: self, size: size } } /// Returns an iterator over `chunk_size` elements of the slice at a @@ -1224,7 +677,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks(&self, chunk_size: usize) -> Chunks<T> { - SliceExt::chunks(self, chunk_size) + assert!(chunk_size != 0); + Chunks { v: self, chunk_size: chunk_size } } /// Returns an iterator over `chunk_size` elements of the slice at a @@ -1256,7 +710,10 @@ macro_rules! slice_core_methods { () => { #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> { - SliceExt::exact_chunks(self, chunk_size) + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + let len = self.len() - rem; + ExactChunks { v: &self[..len], chunk_size: chunk_size} } /// Returns an iterator over `chunk_size` elements of the slice at a time. @@ -1290,7 +747,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<T> { - SliceExt::chunks_mut(self, chunk_size) + assert!(chunk_size != 0); + ChunksMut { v: self, chunk_size: chunk_size } } /// Returns an iterator over `chunk_size` elements of the slice at a time. @@ -1328,7 +786,10 @@ macro_rules! slice_core_methods { () => { #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<T> { - SliceExt::exact_chunks_mut(self, chunk_size) + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + let len = self.len() - rem; + ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size} } /// Divides one slice into two at an index. @@ -1367,7 +828,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split_at(&self, mid: usize) -> (&[T], &[T]) { - SliceExt::split_at(self, mid) + (&self[..mid], &self[mid..]) } /// Divides one mutable slice into two at an index. @@ -1397,7 +858,15 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { - SliceExt::split_at_mut(self, mid) + let len = self.len(); + let ptr = self.as_mut_ptr(); + + unsafe { + assert!(mid <= len); + + (from_raw_parts_mut(ptr, mid), + from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) + } } /// Returns an iterator over subslices separated by elements that match @@ -1445,7 +914,11 @@ macro_rules! slice_core_methods { () => { pub fn split<F>(&self, pred: F) -> Split<T, F> where F: FnMut(&T) -> bool { - SliceExt::split(self, pred) + Split { + v: self, + pred, + finished: false + } } /// Returns an iterator over mutable subslices separated by elements that @@ -1466,7 +939,7 @@ macro_rules! slice_core_methods { () => { pub fn split_mut<F>(&mut self, pred: F) -> SplitMut<T, F> where F: FnMut(&T) -> bool { - SliceExt::split_mut(self, pred) + SplitMut { v: self, pred: pred, finished: false } } /// Returns an iterator over subslices separated by elements that match @@ -1501,7 +974,7 @@ macro_rules! slice_core_methods { () => { pub fn rsplit<F>(&self, pred: F) -> RSplit<T, F> where F: FnMut(&T) -> bool { - SliceExt::rsplit(self, pred) + RSplit { inner: self.split(pred) } } /// Returns an iterator over mutable subslices separated by elements that @@ -1526,7 +999,7 @@ macro_rules! slice_core_methods { () => { pub fn rsplit_mut<F>(&mut self, pred: F) -> RSplitMut<T, F> where F: FnMut(&T) -> bool { - SliceExt::rsplit_mut(self, pred) + RSplitMut { inner: self.split_mut(pred) } } /// Returns an iterator over subslices separated by elements that match @@ -1553,7 +1026,12 @@ macro_rules! slice_core_methods { () => { pub fn splitn<F>(&self, n: usize, pred: F) -> SplitN<T, F> where F: FnMut(&T) -> bool { - SliceExt::splitn(self, n, pred) + SplitN { + inner: GenericSplitN { + iter: self.split(pred), + count: n + } + } } /// Returns an iterator over subslices separated by elements that match @@ -1578,7 +1056,12 @@ macro_rules! slice_core_methods { () => { pub fn splitn_mut<F>(&mut self, n: usize, pred: F) -> SplitNMut<T, F> where F: FnMut(&T) -> bool { - SliceExt::splitn_mut(self, n, pred) + SplitNMut { + inner: GenericSplitN { + iter: self.split_mut(pred), + count: n + } + } } /// Returns an iterator over subslices separated by elements that match @@ -1606,7 +1089,12 @@ macro_rules! slice_core_methods { () => { pub fn rsplitn<F>(&self, n: usize, pred: F) -> RSplitN<T, F> where F: FnMut(&T) -> bool { - SliceExt::rsplitn(self, n, pred) + RSplitN { + inner: GenericSplitN { + iter: self.rsplit(pred), + count: n + } + } } /// Returns an iterator over subslices separated by elements that match @@ -1632,7 +1120,12 @@ macro_rules! slice_core_methods { () => { pub fn rsplitn_mut<F>(&mut self, n: usize, pred: F) -> RSplitNMut<T, F> where F: FnMut(&T) -> bool { - SliceExt::rsplitn_mut(self, n, pred) + RSplitNMut { + inner: GenericSplitN { + iter: self.rsplit_mut(pred), + count: n + } + } } /// Returns `true` if the slice contains an element with the given value. @@ -1648,7 +1141,7 @@ macro_rules! slice_core_methods { () => { pub fn contains(&self, x: &T) -> bool where T: PartialEq { - SliceExt::contains(self, x) + x.slice_contains(self) } /// Returns `true` if `needle` is a prefix of the slice. @@ -1675,7 +1168,8 @@ macro_rules! slice_core_methods { () => { pub fn starts_with(&self, needle: &[T]) -> bool where T: PartialEq { - SliceExt::starts_with(self, needle) + let n = needle.len(); + self.len() >= n && needle == &self[..n] } /// Returns `true` if `needle` is a suffix of the slice. @@ -1702,7 +1196,8 @@ macro_rules! slice_core_methods { () => { pub fn ends_with(&self, needle: &[T]) -> bool where T: PartialEq { - SliceExt::ends_with(self, needle) + let (m, n) = (self.len(), needle.len()); + m >= n && needle == &self[m-n..] } /// Binary searches this sorted slice for a given element. @@ -1731,7 +1226,7 @@ macro_rules! slice_core_methods { () => { pub fn binary_search(&self, x: &T) -> Result<usize, usize> where T: Ord { - SliceExt::binary_search(self, x) + self.binary_search_by(|p| p.cmp(x)) } /// Binary searches this sorted slice with a comparator function. @@ -1767,10 +1262,29 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize> + pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize> where F: FnMut(&'a T) -> Ordering { - SliceExt::binary_search_by(self, f) + let s = self; + let mut size = s.len(); + if size == 0 { + return Err(0); + } + let mut base = 0usize; + while size > 1 { + let half = size / 2; + let mid = base + half; + // mid is always in [0, size), that means mid is >= 0 and < size. + // mid >= 0: by definition + // mid < size: mid = size / 2 + size / 4 + size / 8 ... + let cmp = f(unsafe { s.get_unchecked(mid) }); + base = if cmp == Greater { base } else { mid }; + size -= half; + } + // base is always in [0, size) because base <= mid. + let cmp = f(unsafe { s.get_unchecked(base) }); + if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } + } /// Binary searches this sorted slice with a key extraction function. @@ -1805,11 +1319,11 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] #[inline] - pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result<usize, usize> + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result<usize, usize> where F: FnMut(&'a T) -> B, B: Ord { - SliceExt::binary_search_by_key(self, b, f) + self.binary_search_by(|k| f(k).cmp(b)) } /// Sorts the slice, but may not preserve the order of equal elements. @@ -1843,7 +1357,7 @@ macro_rules! slice_core_methods { () => { pub fn sort_unstable(&mut self) where T: Ord { - SliceExt::sort_unstable(self); + sort::quicksort(self, |a, b| a.lt(b)); } /// Sorts the slice with a comparator function, but may not preserve the order of equal @@ -1878,10 +1392,10 @@ macro_rules! slice_core_methods { () => { /// [pdqsort]: https://github.com/orlp/pdqsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] - pub fn sort_unstable_by<F>(&mut self, compare: F) + pub fn sort_unstable_by<F>(&mut self, mut compare: F) where F: FnMut(&T, &T) -> Ordering { - SliceExt::sort_unstable_by(self, compare); + sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); } /// Sorts the slice with a key extraction function, but may not preserve the order of equal @@ -1910,10 +1424,10 @@ macro_rules! slice_core_methods { () => { /// [pdqsort]: https://github.com/orlp/pdqsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] - pub fn sort_unstable_by_key<K, F>(&mut self, f: F) + pub fn sort_unstable_by_key<K, F>(&mut self, mut f: F) where F: FnMut(&T) -> K, K: Ord { - SliceExt::sort_unstable_by_key(self, f); + sort::quicksort(self, |a, b| f(a).lt(&f(b))); } /// Rotates the slice in-place such that the first `mid` elements of the @@ -1948,7 +1462,13 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] pub fn rotate_left(&mut self, mid: usize) { - SliceExt::rotate_left(self, mid); + assert!(mid <= self.len()); + let k = self.len() - mid; + + unsafe { + let p = self.as_mut_ptr(); + rotate::ptr_rotate(mid, p.offset(mid as isize), k); + } } /// Rotates the slice in-place such that the first `self.len() - k` @@ -1983,7 +1503,13 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] pub fn rotate_right(&mut self, k: usize) { - SliceExt::rotate_right(self, k); + assert!(k <= self.len()); + let mid = self.len() - k; + + unsafe { + let p = self.as_mut_ptr(); + rotate::ptr_rotate(mid, p.offset(mid as isize), k); + } } /// Copies the elements from `src` into `self`. @@ -2040,7 +1566,17 @@ macro_rules! slice_core_methods { () => { /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "clone_from_slice", since = "1.7.0")] pub fn clone_from_slice(&mut self, src: &[T]) where T: Clone { - SliceExt::clone_from_slice(self, src) + assert!(self.len() == src.len(), + "destination and source slices have different lengths"); + // NOTE: We need to explicitly slice them to the same length + // for bounds checking to be elided, and the optimizer will + // generate memcpy for simple cases (for example T = u8). + let len = self.len(); + let src = &src[..len]; + for i in 0..len { + self[i].clone_from(&src[i]); + } + } /// Copies all elements from `src` into `self`, using a memcpy. @@ -2096,7 +1632,12 @@ macro_rules! slice_core_methods { () => { /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "copy_from_slice", since = "1.9.0")] pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy { - SliceExt::copy_from_slice(self, src) + assert!(self.len() == src.len(), + "destination and source slices have different lengths"); + unsafe { + ptr::copy_nonoverlapping( + src.as_ptr(), self.as_mut_ptr(), self.len()); + } } /// Swaps all elements in `self` with those in `other`. @@ -2148,22 +1689,186 @@ macro_rules! slice_core_methods { () => { /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "swap_with_slice", since = "1.27.0")] pub fn swap_with_slice(&mut self, other: &mut [T]) { - SliceExt::swap_with_slice(self, other) + assert!(self.len() == other.len(), + "destination and source slices have different lengths"); + unsafe { + ptr::swap_nonoverlapping( + self.as_mut_ptr(), other.as_mut_ptr(), self.len()); + } } -}} -#[lang = "slice"] -#[cfg(not(test))] -#[cfg(not(stage0))] -impl<T> [T] { - slice_core_methods!(); + /// Function to calculate lenghts of the middle and trailing slice for `align_to{,_mut}`. + #[cfg(not(stage0))] + fn align_to_offsets<U>(&self) -> (usize, usize) { + // What we gonna do about `rest` is figure out what multiple of `U`s we can put in a + // lowest number of `T`s. And how many `T`s we need for each such "multiple". + // + // Consider for example T=u8 U=u16. Then we can put 1 U in 2 Ts. Simple. Now, consider + // for example a case where size_of::<T> = 16, size_of::<U> = 24. We can put 2 Us in + // place of every 3 Ts in the `rest` slice. A bit more complicated. + // + // Formula to calculate this is: + // + // Us = lcm(size_of::<T>, size_of::<U>) / size_of::<U> + // Ts = lcm(size_of::<T>, size_of::<U>) / size_of::<T> + // + // Expanded and simplified: + // + // Us = size_of::<T> / gcd(size_of::<T>, size_of::<U>) + // Ts = size_of::<U> / gcd(size_of::<T>, size_of::<U>) + // + // Luckily since all this is constant-evaluated... performance here matters not! + #[inline] + fn gcd(a: usize, b: usize) -> usize { + // iterative stein’s algorithm + // We should still make this `const fn` (and revert to recursive algorithm if we do) + // because relying on llvm to consteval all this is… well, it makes me + let (ctz_a, mut ctz_b) = unsafe { + if a == 0 { return b; } + if b == 0 { return a; } + (::intrinsics::cttz_nonzero(a), ::intrinsics::cttz_nonzero(b)) + }; + let k = ctz_a.min(ctz_b); + let mut a = a >> ctz_a; + let mut b = b; + loop { + // remove all factors of 2 from b + b >>= ctz_b; + if a > b { + ::mem::swap(&mut a, &mut b); + } + b = b - a; + unsafe { + if b == 0 { + break; + } + ctz_b = ::intrinsics::cttz_nonzero(b); + } + } + return a << k; + } + let gcd: usize = gcd(::mem::size_of::<T>(), ::mem::size_of::<U>()); + let ts: usize = ::mem::size_of::<U>() / gcd; + let us: usize = ::mem::size_of::<T>() / gcd; + + // Armed with this knowledge, we can find how many `U`s we can fit! + let us_len = self.len() / ts * us; + // And how many `T`s will be in the trailing slice! + let ts_len = self.len() % ts; + return (us_len, ts_len); + } + + /// Transmute the slice to a slice of another type, ensuring aligment of the types is + /// maintained. + /// + /// This method splits the slice into three distinct slices: prefix, correctly aligned middle + /// slice of a new type, and the suffix slice. The middle slice will have the greatest length + /// possible for a given type and input slice. + /// + /// This method has no purpose when either input element `T` or output element `U` are + /// zero-sized and will return the original slice without splitting anything. + /// + /// # Unsafety + /// + /// This method is essentially a `transmute` with respect to the elements in the returned + /// middle slice, so all the usual caveats pertaining to `transmute::<T, U>` also apply here. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # #![feature(slice_align_to)] + /// unsafe { + /// let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7]; + /// let (prefix, shorts, suffix) = bytes.align_to::<u16>(); + /// // less_efficient_algorithm_for_bytes(prefix); + /// // more_efficient_algorithm_for_aligned_shorts(shorts); + /// // less_efficient_algorithm_for_bytes(suffix); + /// } + /// ``` + #[unstable(feature = "slice_align_to", issue = "44488")] + #[cfg(not(stage0))] + pub unsafe fn align_to<U>(&self) -> (&[T], &[U], &[T]) { + // Note that most of this function will be constant-evaluated, + if ::mem::size_of::<U>() == 0 || ::mem::size_of::<T>() == 0 { + // handle ZSTs specially, which is – don't handle them at all. + return (self, &[], &[]); + } + + // First, find at what point do we split between the first and 2nd slice. Easy with + // ptr.align_offset. + let ptr = self.as_ptr(); + let offset = ::ptr::align_offset(ptr, ::mem::align_of::<U>()); + if offset > self.len() { + return (self, &[], &[]); + } else { + let (left, rest) = self.split_at(offset); + let (us_len, ts_len) = rest.align_to_offsets::<U>(); + return (left, + from_raw_parts(rest.as_ptr() as *const U, us_len), + from_raw_parts(rest.as_ptr().offset((rest.len() - ts_len) as isize), ts_len)) + } + } + + /// Transmute the slice to a slice of another type, ensuring aligment of the types is + /// maintained. + /// + /// This method splits the slice into three distinct slices: prefix, correctly aligned middle + /// slice of a new type, and the suffix slice. The middle slice will have the greatest length + /// possible for a given type and input slice. + /// + /// This method has no purpose when either input element `T` or output element `U` are + /// zero-sized and will return the original slice without splitting anything. + /// + /// # Unsafety + /// + /// This method is essentially a `transmute` with respect to the elements in the returned + /// middle slice, so all the usual caveats pertaining to `transmute::<T, U>` also apply here. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # #![feature(slice_align_to)] + /// unsafe { + /// let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7]; + /// let (prefix, shorts, suffix) = bytes.align_to_mut::<u16>(); + /// // less_efficient_algorithm_for_bytes(prefix); + /// // more_efficient_algorithm_for_aligned_shorts(shorts); + /// // less_efficient_algorithm_for_bytes(suffix); + /// } + /// ``` + #[unstable(feature = "slice_align_to", issue = "44488")] + #[cfg(not(stage0))] + pub unsafe fn align_to_mut<U>(&mut self) -> (&mut [T], &mut [U], &mut [T]) { + // Note that most of this function will be constant-evaluated, + if ::mem::size_of::<U>() == 0 || ::mem::size_of::<T>() == 0 { + // handle ZSTs specially, which is – don't handle them at all. + return (self, &mut [], &mut []); + } + + // First, find at what point do we split between the first and 2nd slice. Easy with + // ptr.align_offset. + let ptr = self.as_ptr(); + let offset = ::ptr::align_offset(ptr, ::mem::align_of::<U>()); + if offset > self.len() { + return (self, &mut [], &mut []); + } else { + let (left, rest) = self.split_at_mut(offset); + let (us_len, ts_len) = rest.align_to_offsets::<U>(); + let mut_ptr = rest.as_mut_ptr(); + return (left, + from_raw_parts_mut(mut_ptr as *mut U, us_len), + from_raw_parts_mut(mut_ptr.offset((rest.len() - ts_len) as isize), ts_len)) + } + } } -// FIXME: remove (inline) this macro -// when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] -#[unstable(feature = "core_slice_ext", issue = "32110")] -macro_rules! slice_u8_core_methods { () => { +#[lang = "slice_u8"] +#[cfg(not(test))] +impl [u8] { /// Checks if all bytes in this slice are within the ASCII range. #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] @@ -2217,13 +1922,7 @@ macro_rules! slice_u8_core_methods { () => { byte.make_ascii_lowercase(); } } -}} -#[lang = "slice_u8"] -#[cfg(not(test))] -#[cfg(not(stage0))] -impl [u8] { - slice_u8_core_methods!(); } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index df7b2f25a86..82bead0ab46 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -2097,119 +2097,7 @@ mod traits { (..self.end+1).index_mut(slice) } } - -} - -public_in_stage0! { -{ -/// Methods for string slices -#[allow(missing_docs)] -#[doc(hidden)] -#[unstable(feature = "core_str_ext", - reason = "stable interface provided by `impl str` in later crates", - issue = "32110")] } -trait StrExt { - // NB there are no docs here are they're all located on the StrExt trait in - // liballoc, not here. - - #[stable(feature = "core", since = "1.6.0")] - fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn chars(&self) -> Chars; - #[stable(feature = "core", since = "1.6.0")] - fn bytes(&self) -> Bytes; - #[stable(feature = "core", since = "1.6.0")] - fn char_indices(&self) -> CharIndices; - #[stable(feature = "core", since = "1.6.0")] - fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn splitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> SplitN<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn lines(&self) -> Lines; - #[stable(feature = "core", since = "1.6.0")] - #[rustc_deprecated(since = "1.6.0", reason = "use lines() instead now")] - #[allow(deprecated)] - fn lines_any(&self) -> LinesAny; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - fn get<I: SliceIndex<str>>(&self, i: I) -> Option<&I::Output>; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - fn get_mut<I: SliceIndex<str>>(&mut self, i: I) -> Option<&mut I::Output>; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe fn get_unchecked<I: SliceIndex<str>>(&self, i: I) -> &I::Output; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe fn get_unchecked_mut<I: SliceIndex<str>>(&mut self, i: I) -> &mut I::Output; - #[stable(feature = "core", since = "1.6.0")] - unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str; - #[stable(feature = "core", since = "1.6.0")] - unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str; - #[stable(feature = "core", since = "1.6.0")] - fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: DoubleEndedSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str; - #[stable(feature = "core", since = "1.6.0")] - fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "is_char_boundary", since = "1.9.0")] - fn is_char_boundary(&self, index: usize) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn as_bytes(&self) -> &[u8]; - #[stable(feature = "str_mut_extras", since = "1.20.0")] - unsafe fn as_bytes_mut(&mut self) -> &mut [u8]; - #[stable(feature = "core", since = "1.6.0")] - fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>; - #[stable(feature = "core", since = "1.6.0")] - fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> - where P::Searcher: ReverseSearcher<'a>; - fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>; - #[stable(feature = "core", since = "1.6.0")] - fn split_at(&self, mid: usize) -> (&str, &str); - #[stable(feature = "core", since = "1.6.0")] - fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str); - #[stable(feature = "core", since = "1.6.0")] - fn as_ptr(&self) -> *const u8; - #[stable(feature = "core", since = "1.6.0")] - fn len(&self) -> usize; - #[stable(feature = "core", since = "1.6.0")] - fn is_empty(&self) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn parse<T: FromStr>(&self) -> Result<T, T::Err>; - #[stable(feature = "split_whitespace", since = "1.1.0")] - fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>; - #[stable(feature = "rust1", since = "1.0.0")] - fn trim(&self) -> &str; - #[stable(feature = "rust1", since = "1.0.0")] - fn trim_left(&self) -> &str; - #[stable(feature = "rust1", since = "1.0.0")] - fn trim_right(&self) -> &str; -}} // truncate `&str` to length at most equal to `max` // return `true` if it were truncated, and the new str. @@ -2255,307 +2143,9 @@ fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { index, ch, char_range, s_trunc, ellipsis); } -#[stable(feature = "core", since = "1.6.0")] -impl StrExt for str { - #[inline] - fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - pat.is_contained_in(self) - } - - #[inline] - fn chars(&self) -> Chars { - Chars{iter: self.as_bytes().iter()} - } - - #[inline] - fn bytes(&self) -> Bytes { - Bytes(self.as_bytes().iter().cloned()) - } - - #[inline] - fn char_indices(&self) -> CharIndices { - CharIndices { front_offset: 0, iter: self.chars() } - } - - #[inline] - fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { - Split(SplitInternal { - start: 0, - end: self.len(), - matcher: pat.into_searcher(self), - allow_trailing_empty: true, - finished: false, - }) - } - - #[inline] - fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RSplit(self.split(pat).0) - } - - #[inline] - fn splitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> SplitN<'a, P> { - SplitN(SplitNInternal { - iter: self.split(pat).0, - count, - }) - } - - #[inline] - fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RSplitN(self.splitn(count, pat).0) - } - - #[inline] - fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { - SplitTerminator(SplitInternal { - allow_trailing_empty: false, - ..self.split(pat).0 - }) - } - - #[inline] - fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RSplitTerminator(self.split_terminator(pat).0) - } - - #[inline] - fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { - Matches(MatchesInternal(pat.into_searcher(self))) - } - - #[inline] - fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RMatches(self.matches(pat).0) - } - - #[inline] - fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { - MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) - } - - #[inline] - fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RMatchIndices(self.match_indices(pat).0) - } - #[inline] - fn lines(&self) -> Lines { - Lines(self.split_terminator('\n').map(LinesAnyMap)) - } - - #[inline] - #[allow(deprecated)] - fn lines_any(&self) -> LinesAny { - LinesAny(self.lines()) - } - - #[inline] - fn get<I: SliceIndex<str>>(&self, i: I) -> Option<&I::Output> { - i.get(self) - } - - #[inline] - fn get_mut<I: SliceIndex<str>>(&mut self, i: I) -> Option<&mut I::Output> { - i.get_mut(self) - } - - #[inline] - unsafe fn get_unchecked<I: SliceIndex<str>>(&self, i: I) -> &I::Output { - i.get_unchecked(self) - } - - #[inline] - unsafe fn get_unchecked_mut<I: SliceIndex<str>>(&mut self, i: I) -> &mut I::Output { - i.get_unchecked_mut(self) - } - - #[inline] - unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { - (begin..end).get_unchecked(self) - } - - #[inline] - unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { - (begin..end).get_unchecked_mut(self) - } - - #[inline] - fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - pat.is_prefix_of(self) - } - - #[inline] - fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool - where P::Searcher: ReverseSearcher<'a> - { - pat.is_suffix_of(self) - } - - #[inline] - fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: DoubleEndedSearcher<'a> - { - let mut i = 0; - let mut j = 0; - let mut matcher = pat.into_searcher(self); - if let Some((a, b)) = matcher.next_reject() { - i = a; - j = b; // Remember earliest known match, correct it below if - // last match is different - } - if let Some((_, b)) = matcher.next_reject_back() { - j = b; - } - unsafe { - // Searcher is known to return valid indices - self.slice_unchecked(i, j) - } - } - - #[inline] - fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { - let mut i = self.len(); - let mut matcher = pat.into_searcher(self); - if let Some((a, _)) = matcher.next_reject() { - i = a; - } - unsafe { - // Searcher is known to return valid indices - self.slice_unchecked(i, self.len()) - } - } - - #[inline] - fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: ReverseSearcher<'a> - { - let mut j = 0; - let mut matcher = pat.into_searcher(self); - if let Some((_, b)) = matcher.next_reject_back() { - j = b; - } - unsafe { - // Searcher is known to return valid indices - self.slice_unchecked(0, j) - } - } - - #[inline] - fn is_char_boundary(&self, index: usize) -> bool { - // 0 and len are always ok. - // Test for 0 explicitly so that it can optimize out the check - // easily and skip reading string data for that case. - if index == 0 || index == self.len() { return true; } - match self.as_bytes().get(index) { - None => false, - // This is bit magic equivalent to: b < 128 || b >= 192 - Some(&b) => (b as i8) >= -0x40, - } - } - - #[inline] - fn as_bytes(&self) -> &[u8] { - unsafe { &*(self as *const str as *const [u8]) } - } - - #[inline] - unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { - &mut *(self as *mut str as *mut [u8]) - } - - fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> { - pat.into_searcher(self).next_match().map(|(i, _)| i) - } - - fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> - where P::Searcher: ReverseSearcher<'a> - { - pat.into_searcher(self).next_match_back().map(|(i, _)| i) - } - - fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> { - self.find(pat) - } - - #[inline] - fn split_at(&self, mid: usize) -> (&str, &str) { - // is_char_boundary checks that the index is in [0, .len()] - if self.is_char_boundary(mid) { - unsafe { - (self.slice_unchecked(0, mid), - self.slice_unchecked(mid, self.len())) - } - } else { - slice_error_fail(self, 0, mid) - } - } - - fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { - // is_char_boundary checks that the index is in [0, .len()] - if self.is_char_boundary(mid) { - let len = self.len(); - let ptr = self.as_ptr() as *mut u8; - unsafe { - (from_raw_parts_mut(ptr, mid), - from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) - } - } else { - slice_error_fail(self, 0, mid) - } - } - - #[inline] - fn as_ptr(&self) -> *const u8 { - self as *const str as *const u8 - } - - #[inline] - fn len(&self) -> usize { - self.as_bytes().len() - } - - #[inline] - fn is_empty(&self) -> bool { self.len() == 0 } - - #[inline] - fn parse<T: FromStr>(&self) -> Result<T, T::Err> { FromStr::from_str(self) } - - #[inline] - fn split_whitespace(&self) -> SplitWhitespace { - SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } - } - - #[inline] - fn trim(&self) -> &str { - self.trim_matches(|c: char| c.is_whitespace()) - } - - #[inline] - fn trim_left(&self) -> &str { - self.trim_left_matches(|c: char| c.is_whitespace()) - } - - #[inline] - fn trim_right(&self) -> &str { - self.trim_right_matches(|c: char| c.is_whitespace()) - } -} - -// FIXME: remove (inline) this macro and the SliceExt trait -// when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] -#[unstable(feature = "core_str_ext", issue = "32110")] -macro_rules! str_core_methods { () => { +#[lang = "str"] +#[cfg(not(test))] +impl str { /// Returns the length of `self`. /// /// This length is in bytes, not [`char`]s or graphemes. In other words, @@ -2577,7 +2167,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn len(&self) -> usize { - StrExt::len(self) + self.as_bytes().len() } /// Returns `true` if `self` has a length of zero bytes. @@ -2596,7 +2186,7 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn is_empty(&self) -> bool { - StrExt::is_empty(self) + self.len() == 0 } /// Checks that `index`-th byte lies at the start and/or end of a @@ -2626,7 +2216,15 @@ macro_rules! str_core_methods { () => { #[stable(feature = "is_char_boundary", since = "1.9.0")] #[inline] pub fn is_char_boundary(&self, index: usize) -> bool { - StrExt::is_char_boundary(self, index) + // 0 and len are always ok. + // Test for 0 explicitly so that it can optimize out the check + // easily and skip reading string data for that case. + if index == 0 || index == self.len() { return true; } + match self.as_bytes().get(index) { + None => false, + // This is bit magic equivalent to: b < 128 || b >= 192 + Some(&b) => (b as i8) >= -0x40, + } } /// Converts a string slice to a byte slice. To convert the byte slice back @@ -2645,7 +2243,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline(always)] pub fn as_bytes(&self) -> &[u8] { - StrExt::as_bytes(self) + unsafe { &*(self as *const str as *const [u8]) } } /// Converts a mutable string slice to a mutable byte slice. To convert the @@ -2684,7 +2282,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_mut_extras", since = "1.20.0")] #[inline(always)] pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { - StrExt::as_bytes_mut(self) + &mut *(self as *mut str as *mut [u8]) } /// Converts a string slice to a raw pointer. @@ -2706,7 +2304,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn as_ptr(&self) -> *const u8 { - StrExt::as_ptr(self) + self as *const str as *const u8 } /// Returns a subslice of `str`. @@ -2733,7 +2331,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub fn get<I: SliceIndex<str>>(&self, i: I) -> Option<&I::Output> { - StrExt::get(self, i) + i.get(self) } /// Returns a mutable subslice of `str`. @@ -2767,7 +2365,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub fn get_mut<I: SliceIndex<str>>(&mut self, i: I) -> Option<&mut I::Output> { - StrExt::get_mut(self, i) + i.get_mut(self) } /// Returns a unchecked subslice of `str`. @@ -2799,7 +2397,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub unsafe fn get_unchecked<I: SliceIndex<str>>(&self, i: I) -> &I::Output { - StrExt::get_unchecked(self, i) + i.get_unchecked(self) } /// Returns a mutable, unchecked subslice of `str`. @@ -2831,7 +2429,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub unsafe fn get_unchecked_mut<I: SliceIndex<str>>(&mut self, i: I) -> &mut I::Output { - StrExt::get_unchecked_mut(self, i) + i.get_unchecked_mut(self) } /// Creates a string slice from another string slice, bypassing safety @@ -2880,7 +2478,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { - StrExt::slice_unchecked(self, begin, end) + (begin..end).get_unchecked(self) } /// Creates a string slice from another string slice, bypassing safety @@ -2910,7 +2508,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_slice_mut", since = "1.5.0")] #[inline] pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { - StrExt::slice_mut_unchecked(self, begin, end) + (begin..end).get_unchecked_mut(self) } /// Divide one string slice into two at an index. @@ -2946,7 +2544,15 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "str_split_at", since = "1.4.0")] pub fn split_at(&self, mid: usize) -> (&str, &str) { - StrExt::split_at(self, mid) + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + unsafe { + (self.slice_unchecked(0, mid), + self.slice_unchecked(mid, self.len())) + } + } else { + slice_error_fail(self, 0, mid) + } } /// Divide one mutable string slice into two at an index. @@ -2983,7 +2589,17 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "str_split_at", since = "1.4.0")] pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { - StrExt::split_at_mut(self, mid) + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + let len = self.len(); + let ptr = self.as_ptr() as *mut u8; + unsafe { + (from_raw_parts_mut(ptr, mid), + from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) + } + } else { + slice_error_fail(self, 0, mid) + } } /// Returns an iterator over the [`char`]s of a string slice. @@ -3035,8 +2651,9 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chars(&self) -> Chars { - StrExt::chars(self) + Chars{iter: self.as_bytes().iter()} } + /// Returns an iterator over the [`char`]s of a string slice, and their /// positions. /// @@ -3091,7 +2708,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn char_indices(&self) -> CharIndices { - StrExt::char_indices(self) + CharIndices { front_offset: 0, iter: self.chars() } } /// An iterator over the bytes of a string slice. @@ -3116,7 +2733,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn bytes(&self) -> Bytes { - StrExt::bytes(self) + Bytes(self.as_bytes().iter().cloned()) } /// Split a string slice by whitespace. @@ -3156,7 +2773,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "split_whitespace", since = "1.1.0")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace { - StrExt::split_whitespace(self) + SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } } /// An iterator over the lines of a string, as string slices. @@ -3198,7 +2815,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn lines(&self) -> Lines { - StrExt::lines(self) + Lines(self.split_terminator('\n').map(LinesAnyMap)) } /// An iterator over the lines of a string. @@ -3207,7 +2824,7 @@ macro_rules! str_core_methods { () => { #[inline] #[allow(deprecated)] pub fn lines_any(&self) -> LinesAny { - StrExt::lines_any(self) + LinesAny(self.lines()) } /// Returns an iterator of `u16` over the string encoded as UTF-16. @@ -3226,7 +2843,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "encode_utf16", since = "1.8.0")] pub fn encode_utf16(&self) -> EncodeUtf16 { - EncodeUtf16::new(self) + EncodeUtf16 { chars: self.chars(), extra: 0 } } /// Returns `true` if the given pattern matches a sub-slice of @@ -3247,7 +2864,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - StrExt::contains(self, pat) + pat.is_contained_in(self) } /// Returns `true` if the given pattern matches a prefix of this @@ -3267,7 +2884,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - StrExt::starts_with(self, pat) + pat.is_prefix_of(self) } /// Returns `true` if the given pattern matches a suffix of this @@ -3289,7 +2906,7 @@ macro_rules! str_core_methods { () => { pub fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool where P::Searcher: ReverseSearcher<'a> { - StrExt::ends_with(self, pat) + pat.is_suffix_of(self) } /// Returns the byte index of the first character of this string slice that @@ -3337,7 +2954,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> { - StrExt::find(self, pat) + pat.into_searcher(self).next_match().map(|(i, _)| i) } /// Returns the byte index of the last character of this string slice that @@ -3384,7 +3001,7 @@ macro_rules! str_core_methods { () => { pub fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> where P::Searcher: ReverseSearcher<'a> { - StrExt::rfind(self, pat) + pat.into_searcher(self).next_match_back().map(|(i, _)| i) } /// An iterator over substrings of this string slice, separated by @@ -3496,7 +3113,13 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { - StrExt::split(self, pat) + Split(SplitInternal { + start: 0, + end: self.len(), + matcher: pat.into_searcher(self), + allow_trailing_empty: true, + finished: false, + }) } /// An iterator over substrings of the given string slice, separated by @@ -3548,7 +3171,7 @@ macro_rules! str_core_methods { () => { pub fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rsplit(self, pat) + RSplit(self.split(pat).0) } /// An iterator over substrings of the given string slice, separated by @@ -3593,7 +3216,10 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { - StrExt::split_terminator(self, pat) + SplitTerminator(SplitInternal { + allow_trailing_empty: false, + ..self.split(pat).0 + }) } /// An iterator over substrings of `self`, separated by characters @@ -3639,7 +3265,7 @@ macro_rules! str_core_methods { () => { pub fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rsplit_terminator(self, pat) + RSplitTerminator(self.split_terminator(pat).0) } /// An iterator over substrings of the given string slice, separated by a @@ -3690,7 +3316,10 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, P> { - StrExt::splitn(self, n, pat) + SplitN(SplitNInternal { + iter: self.split(pat).0, + count: n, + }) } /// An iterator over substrings of this string slice, separated by a @@ -3740,7 +3369,7 @@ macro_rules! str_core_methods { () => { pub fn rsplitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rsplitn(self, n, pat) + RSplitN(self.splitn(n, pat).0) } /// An iterator over the disjoint matches of a pattern within the given string @@ -3779,7 +3408,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_matches", since = "1.2.0")] #[inline] pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { - StrExt::matches(self, pat) + Matches(MatchesInternal(pat.into_searcher(self))) } /// An iterator over the disjoint matches of a pattern within this string slice, @@ -3818,7 +3447,7 @@ macro_rules! str_core_methods { () => { pub fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rmatches(self, pat) + RMatches(self.matches(pat).0) } /// An iterator over the disjoint matches of a pattern within this string @@ -3862,7 +3491,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] pub fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { - StrExt::match_indices(self, pat) + MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) } /// An iterator over the disjoint matches of a pattern within `self`, @@ -3907,7 +3536,7 @@ macro_rules! str_core_methods { () => { pub fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rmatch_indices(self, pat) + RMatchIndices(self.match_indices(pat).0) } /// Returns a string slice with leading and trailing whitespace removed. @@ -3926,7 +3555,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim(&self) -> &str { - StrExt::trim(self) + self.trim_matches(|c: char| c.is_whitespace()) } /// Returns a string slice with leading whitespace removed. @@ -3962,7 +3591,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_left(&self) -> &str { - StrExt::trim_left(self) + self.trim_left_matches(|c: char| c.is_whitespace()) } /// Returns a string slice with trailing whitespace removed. @@ -3998,7 +3627,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_right(&self) -> &str { - StrExt::trim_right(self) + self.trim_right_matches(|c: char| c.is_whitespace()) } /// Returns a string slice with all prefixes and suffixes that match a @@ -4030,7 +3659,21 @@ macro_rules! str_core_methods { () => { pub fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str where P::Searcher: DoubleEndedSearcher<'a> { - StrExt::trim_matches(self, pat) + let mut i = 0; + let mut j = 0; + let mut matcher = pat.into_searcher(self); + if let Some((a, b)) = matcher.next_reject() { + i = a; + j = b; // Remember earliest known match, correct it below if + // last match is different + } + if let Some((_, b)) = matcher.next_reject_back() { + j = b; + } + unsafe { + // Searcher is known to return valid indices + self.slice_unchecked(i, j) + } } /// Returns a string slice with all prefixes that match a pattern @@ -4061,7 +3704,15 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { - StrExt::trim_left_matches(self, pat) + let mut i = self.len(); + let mut matcher = pat.into_searcher(self); + if let Some((a, _)) = matcher.next_reject() { + i = a; + } + unsafe { + // Searcher is known to return valid indices + self.slice_unchecked(i, self.len()) + } } /// Returns a string slice with all suffixes that match a pattern @@ -4100,7 +3751,15 @@ macro_rules! str_core_methods { () => { pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str where P::Searcher: ReverseSearcher<'a> { - StrExt::trim_right_matches(self, pat) + let mut j = 0; + let mut matcher = pat.into_searcher(self); + if let Some((_, b)) = matcher.next_reject_back() { + j = b; + } + unsafe { + // Searcher is known to return valid indices + self.slice_unchecked(0, j) + } } /// Parses this string slice into another type. @@ -4150,7 +3809,7 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> { - StrExt::parse(self) + FromStr::from_str(self) } /// Checks if all characters in this string are within the ASCII range. @@ -4220,16 +3879,8 @@ macro_rules! str_core_methods { () => { let me = unsafe { self.as_bytes_mut() }; me.make_ascii_lowercase() } -}} - -#[lang = "str"] -#[cfg(not(test))] -#[cfg(not(stage0))] -impl str { - str_core_methods!(); } - #[stable(feature = "rust1", since = "1.0.0")] impl AsRef<[u8]> for str { #[inline] @@ -4332,17 +3983,6 @@ pub struct EncodeUtf16<'a> { extra: u16, } -// FIXME: remove (inline) this method -// when updating to a bootstrap compiler that has the new lang items. -// For grepping purpose: #[cfg(stage0)] -impl<'a> EncodeUtf16<'a> { - #[unstable(feature = "core_str_ext", issue = "32110")] - #[doc(hidden)] - pub fn new(s: &'a str) -> Self { - EncodeUtf16 { chars: s.chars(), extra: 0 } - } -} - #[stable(feature = "collection_debug", since = "1.17.0")] impl<'a> fmt::Debug for EncodeUtf16<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 7fb4b503c01..13189d532ab 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -41,9 +41,9 @@ #![feature(try_from)] #![feature(try_trait)] #![feature(exact_chunks)] -#![cfg_attr(stage0, feature(atomic_nand))] +#![feature(slice_align_to)] +#![feature(align_offset)] #![feature(reverse_bits)] -#![feature(inclusive_range_methods)] #![feature(iterator_find_map)] #![feature(slice_internals)] diff --git a/src/libcore/tests/num/uint_macros.rs b/src/libcore/tests/num/uint_macros.rs index 257f6ea20d4..ca6906f7310 100644 --- a/src/libcore/tests/num/uint_macros.rs +++ b/src/libcore/tests/num/uint_macros.rs @@ -98,7 +98,6 @@ mod tests { } #[test] - #[cfg(not(stage0))] fn test_reverse_bits() { assert_eq!(A.reverse_bits().reverse_bits(), A); assert_eq!(B.reverse_bits().reverse_bits(), B); diff --git a/src/libcore/tests/ptr.rs b/src/libcore/tests/ptr.rs index 00f87336f3c..31bc1d67768 100644 --- a/src/libcore/tests/ptr.rs +++ b/src/libcore/tests/ptr.rs @@ -296,3 +296,92 @@ fn write_unaligned_drop() { } DROPS.with(|d| assert_eq!(*d.borrow(), [0])); } + +#[test] +fn align_offset_zst() { + // For pointers of stride = 0, the pointer is already aligned or it cannot be aligned at + // all, because no amount of elements will align the pointer. + let mut p = 1; + while p < 1024 { + assert_eq!((p as *const ()).align_offset(p), 0); + if p != 1 { + assert_eq!(((p + 1) as *const ()).align_offset(p), !0); + } + p = (p + 1).next_power_of_two(); + } +} + +#[test] +fn align_offset_stride1() { + // For pointers of stride = 1, the pointer can always be aligned. The offset is equal to + // number of bytes. + let mut align = 1; + while align < 1024 { + for ptr in 1..2*align { + let expected = ptr % align; + let offset = if expected == 0 { 0 } else { align - expected }; + assert_eq!((ptr as *const u8).align_offset(align), offset, + "ptr = {}, align = {}, size = 1", ptr, align); + } + align = (align + 1).next_power_of_two(); + } +} + +#[test] +fn align_offset_weird_strides() { + #[repr(packed)] + struct A3(u16, u8); + struct A4(u32); + #[repr(packed)] + struct A5(u32, u8); + #[repr(packed)] + struct A6(u32, u16); + #[repr(packed)] + struct A7(u32, u16, u8); + #[repr(packed)] + struct A8(u32, u32); + #[repr(packed)] + struct A9(u32, u32, u8); + #[repr(packed)] + struct A10(u32, u32, u16); + + unsafe fn test_weird_stride<T>(ptr: *const T, align: usize) -> bool { + let numptr = ptr as usize; + let mut expected = usize::max_value(); + // Naive but definitely correct way to find the *first* aligned element of stride::<T>. + for el in 0..align { + if (numptr + el * ::std::mem::size_of::<T>()) % align == 0 { + expected = el; + break; + } + } + let got = ptr.align_offset(align); + if got != expected { + eprintln!("aligning {:p} (with stride of {}) to {}, expected {}, got {}", ptr, + ::std::mem::size_of::<T>(), align, expected, got); + return true; + } + return false; + } + + // For pointers of stride != 1, we verify the algorithm against the naivest possible + // implementation + let mut align = 1; + let mut x = false; + while align < 1024 { + for ptr in 1usize..4*align { + unsafe { + x |= test_weird_stride::<A3>(ptr as *const A3, align); + x |= test_weird_stride::<A4>(ptr as *const A4, align); + x |= test_weird_stride::<A5>(ptr as *const A5, align); + x |= test_weird_stride::<A6>(ptr as *const A6, align); + x |= test_weird_stride::<A7>(ptr as *const A7, align); + x |= test_weird_stride::<A8>(ptr as *const A8, align); + x |= test_weird_stride::<A9>(ptr as *const A9, align); + x |= test_weird_stride::<A10>(ptr as *const A10, align); + } + } + align = (align + 1).next_power_of_two(); + } + assert!(!x); +} diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 19b5c86c474..fcd79222e16 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -812,3 +812,37 @@ pub mod memchr { } } } + +#[test] +fn test_align_to_simple() { + let bytes = [1u8, 2, 3, 4, 5, 6, 7]; + let (prefix, aligned, suffix) = unsafe { bytes.align_to::<u16>() }; + assert_eq!(aligned.len(), 3); + assert!(prefix == [1] || suffix == [7]); + let expect1 = [1 << 8 | 2, 3 << 8 | 4, 5 << 8 | 6]; + let expect2 = [1 | 2 << 8, 3 | 4 << 8, 5 | 6 << 8]; + let expect3 = [2 << 8 | 3, 4 << 8 | 5, 6 << 8 | 7]; + let expect4 = [2 | 3 << 8, 4 | 5 << 8, 6 | 7 << 8]; + assert!(aligned == expect1 || aligned == expect2 || aligned == expect3 || aligned == expect4, + "aligned={:?} expected={:?} || {:?} || {:?} || {:?}", + aligned, expect1, expect2, expect3, expect4); +} + +#[test] +fn test_align_to_zst() { + let bytes = [1, 2, 3, 4, 5, 6, 7]; + let (prefix, aligned, suffix) = unsafe { bytes.align_to::<()>() }; + assert_eq!(aligned.len(), 0); + assert!(prefix == [1, 2, 3, 4, 5, 6, 7] || suffix == [1, 2, 3, 4, 5, 6, 7]); +} + +#[test] +fn test_align_to_non_trivial() { + #[repr(align(8))] struct U64(u64, u64); + #[repr(align(8))] struct U64U64U32(u64, u64, u32); + let data = [U64(1, 2), U64(3, 4), U64(5, 6), U64(7, 8), U64(9, 10), U64(11, 12), U64(13, 14), + U64(15, 16)]; + let (prefix, aligned, suffix) = unsafe { data.align_to::<U64U64U32>() }; + assert_eq!(aligned.len(), 4); + assert_eq!(prefix.len() + suffix.len(), 2); +} diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index a551b1b770a..a77751d65d0 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -127,6 +127,14 @@ pub enum Count<'a> { CountImplied, } +pub struct ParseError { + pub description: string::String, + pub note: Option<string::String>, + pub label: string::String, + pub start: usize, + pub end: usize, +} + /// The parser structure for interpreting the input format string. This is /// modeled as an iterator over `Piece` structures to form a stream of tokens /// being output. @@ -137,7 +145,7 @@ pub struct Parser<'a> { input: &'a str, cur: iter::Peekable<str::CharIndices<'a>>, /// Error messages accumulated during parsing - pub errors: Vec<(string::String, Option<string::String>)>, + pub errors: Vec<ParseError>, /// Current position of implicit positional argument pointer curarg: usize, } @@ -160,12 +168,17 @@ impl<'a> Iterator for Parser<'a> { } '}' => { self.cur.next(); + let pos = pos + 1; if self.consume('}') { - Some(String(self.string(pos + 1))) + Some(String(self.string(pos))) } else { - self.err_with_note("unmatched `}` found", - "if you intended to print `}`, \ - you can escape it using `}}`"); + self.err_with_note( + "unmatched `}` found", + "unmatched `}`", + "if you intended to print `}`, you can escape it using `}}`", + pos, + pos, + ); None } } @@ -191,15 +204,40 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err(&mut self, msg: &str) { - self.errors.push((msg.to_owned(), None)); + fn err<S1: Into<string::String>, S2: Into<string::String>>( + &mut self, + description: S1, + label: S2, + start: usize, + end: usize, + ) { + self.errors.push(ParseError { + description: description.into(), + note: None, + label: label.into(), + start, + end, + }); } /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err_with_note(&mut self, msg: &str, note: &str) { - self.errors.push((msg.to_owned(), Some(note.to_owned()))); + fn err_with_note<S1: Into<string::String>, S2: Into<string::String>, S3: Into<string::String>>( + &mut self, + description: S1, + label: S2, + note: S3, + start: usize, + end: usize, + ) { + self.errors.push(ParseError { + description: description.into(), + note: Some(note.into()), + label: label.into(), + start, + end, + }); } /// Optionally consumes the specified character. If the character is not at @@ -222,19 +260,26 @@ impl<'a> Parser<'a> { /// found, an error is emitted. fn must_consume(&mut self, c: char) { self.ws(); - if let Some(&(_, maybe)) = self.cur.peek() { + if let Some(&(pos, maybe)) = self.cur.peek() { if c == maybe { self.cur.next(); } else { - self.err(&format!("expected `{:?}`, found `{:?}`", c, maybe)); + self.err(format!("expected `{:?}`, found `{:?}`", c, maybe), + format!("expected `{}`", c), + pos + 1, + pos + 1); } } else { - let msg = &format!("expected `{:?}` but string was terminated", c); + let msg = format!("expected `{:?}` but string was terminated", c); + let pos = self.input.len() + 1; // point at closing `"` if c == '}' { self.err_with_note(msg, - "if you intended to print `{`, you can escape it using `{{`"); + format!("expected `{:?}`", c), + "if you intended to print `{`, you can escape it using `{{`", + pos, + pos); } else { - self.err(msg); + self.err(msg, format!("expected `{:?}`", c), pos, pos); } } } @@ -300,6 +345,15 @@ impl<'a> Parser<'a> { } else { match self.cur.peek() { Some(&(_, c)) if c.is_alphabetic() => Some(ArgumentNamed(self.word())), + Some(&(pos, c)) if c == '_' => { + let invalid_name = self.string(pos); + self.err_with_note(format!("invalid argument name `{}`", invalid_name), + "invalid argument name", + "argument names cannot start with an underscore", + pos + 1, // add 1 to account for leading `{` + pos + 1 + invalid_name.len()); + Some(ArgumentNamed(invalid_name)) + }, // This is an `ArgumentNext`. // Record the fact and do the resolution after parsing the diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 610a9a2a394..bcab6168096 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -818,7 +818,7 @@ impl Ident { pub fn new_raw(string: &str, span: Span) -> Ident { let mut ident = Ident::new(string, span); if ident.sym == keywords::Underscore.name() || - token::is_path_segment_keyword(ast::Ident::with_empty_ctxt(ident.sym)) { + ast::Ident::with_empty_ctxt(ident.sym).is_path_segment_keyword() { panic!("`{:?}` is not a valid raw identifier", string) } ident.is_raw = true; diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 0f4871954d6..45d429612a1 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -593,6 +593,7 @@ impl<'a> LoweringContext<'a> { span: Some(span), allow_internal_unstable: true, allow_internal_unsafe: false, + edition: codemap::hygiene::default_edition(), }, }); span.with_ctxt(SyntaxContext::empty().apply_mark(mark)) diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 9cd9e0dce54..940a68e8ce5 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -13,7 +13,7 @@ pub use self::AnnNode::*; use rustc_target::spec::abi::Abi; use syntax::ast; use syntax::codemap::{CodeMap, Spanned}; -use syntax::parse::{token, ParseSess}; +use syntax::parse::ParseSess; use syntax::parse::lexer::comments; use syntax::print::pp::{self, Breaks}; use syntax::print::pp::Breaks::{Consistent, Inconsistent}; @@ -1559,7 +1559,7 @@ impl<'a> State<'a> { } pub fn print_name(&mut self, name: ast::Name) -> io::Result<()> { - if token::is_raw_guess(ast::Ident::with_empty_ctxt(name)) { + if name.to_ident().is_raw_guess() { self.s.word(&format!("r#{}", name))?; } else { self.s.word(&name.as_str())?; diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index f56d701b028..3a37c1c18c8 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -132,6 +132,15 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability { }); impl<'a> HashStable<StableHashingContext<'a>> +for ::syntax::edition::Edition { + fn hash_stable<W: StableHasherResult>(&self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher<W>) { + mem::discriminant(self).hash_stable(hcx, hasher); + } +} + +impl<'a> HashStable<StableHashingContext<'a>> for ::syntax::attr::StabilityLevel { fn hash_stable<W: StableHasherResult>(&self, hcx: &mut StableHashingContext<'a>, @@ -389,6 +398,7 @@ impl_stable_hash_for!(struct ::syntax_pos::hygiene::NameAndSpan { format, allow_internal_unstable, allow_internal_unsafe, + edition, span }); diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 31dce2a14c2..1b16d93ef27 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -132,7 +132,7 @@ for ty::RegionKind { ty::ReLateBound(..) | ty::ReVar(..) | ty::ReSkolemized(..) => { - bug!("TypeIdHasher: unexpected region {:?}", *self) + bug!("StableHasher: unexpected region {:?}", *self) } } } @@ -401,8 +401,9 @@ for ::mir::interpret::ConstValue<'gcx> { a.hash_stable(hcx, hasher); b.hash_stable(hcx, hasher); } - ByRef(alloc) => { + ByRef(alloc, offset) => { alloc.hash_stable(hcx, hasher); + offset.hash_stable(hcx, hasher); } } } diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index c388fa21371..99d7b4eaf7d 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -318,7 +318,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // should think carefully about whether it needs to be cleared // or updated in some way. let RegionConstraintCollector { - var_infos, + var_infos: _, data, lubs, glbs, @@ -338,10 +338,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // un-unified" state. Note that when we unify `a` and `b`, we // also insert `a <= b` and a `b <= a` edges, so the // `RegionConstraintData` contains the relationship here. - *unification_table = ut::UnificationTable::new(); - for vid in var_infos.indices() { - unification_table.new_key(unify_key::RegionVidKey { min_vid: vid }); - } + unification_table.reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid }); mem::replace(data, RegionConstraintData::default()) } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index ac6ff6831ad..1d53a305193 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -46,7 +46,6 @@ #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(entry_or_default)] -#![cfg_attr(stage0, feature(dyn_trait))] #![feature(from_ref)] #![feature(fs_read_write)] #![feature(iterator_find_map)] @@ -69,7 +68,6 @@ #![feature(trusted_len)] #![feature(catch_expr)] #![feature(test)] -#![feature(inclusive_range_methods)] #![recursion_limit="512"] diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 5ebea52f3c5..ac2fa8515bc 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -27,7 +27,7 @@ declare_lint! { declare_lint! { pub CONST_ERR, - Warn, + Deny, "constant evaluation detected erroneous expression" } @@ -177,12 +177,6 @@ declare_lint! { } declare_lint! { - pub LEGACY_IMPORTS, - Deny, - "detects names that resolve to ambiguous glob imports with RFC 1560" -} - -declare_lint! { pub LEGACY_CONSTRUCTOR_VISIBILITY, Deny, "detects use of struct constructors that would be invisible with new visibility rules" @@ -279,6 +273,12 @@ declare_lint! { "detects name collision with an existing but unstable method" } +declare_lint! { + pub UNUSED_LABELS, + Allow, + "detects labels that are never used" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -314,7 +314,6 @@ impl LintPass for HardwiredLints { SAFE_PACKED_BORROWS, PATTERNS_IN_FNS_WITHOUT_BODY, LEGACY_DIRECTORY_OWNERSHIP, - LEGACY_IMPORTS, LEGACY_CONSTRUCTOR_VISIBILITY, MISSING_FRAGMENT_SPECIFIER, PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES, @@ -325,6 +324,7 @@ impl LintPass for HardwiredLints { UNUSED_MUT, SINGLE_USE_LIFETIME, UNUSED_LIFETIME, + UNUSED_LABELS, TYVAR_BEHIND_RAW_POINTER, ELIDED_LIFETIME_IN_PATH, BARE_TRAIT_OBJECT, diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 61c8470b616..518021f4125 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -34,6 +34,7 @@ use session::search_paths::PathKind; use std::any::Any; use std::path::{Path, PathBuf}; use syntax::ast; +use syntax::edition::Edition; use syntax::ext::base::SyntaxExtension; use syntax::symbol::Symbol; use syntax_pos::Span; @@ -235,6 +236,7 @@ pub trait CrateStore { fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol; fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator; fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh; + fn crate_edition_untracked(&self, cnum: CrateNum) -> Edition; fn struct_field_names_untracked(&self, def: DefId) -> Vec<ast::Name>; fn item_children_untracked(&self, did: DefId, sess: &Session) -> Vec<def::Export>; fn load_macro_untracked(&self, did: DefId, sess: &Session) -> LoadedMacro; @@ -309,6 +311,7 @@ impl CrateStore for DummyCrateStore { bug!("crate_disambiguator") } fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh { bug!("crate_hash") } + fn crate_edition_untracked(&self, cnum: CrateNum) -> Edition { bug!("crate_edition_untracked") } // resolve fn def_key(&self, def: DefId) -> DefKey { bug!("def_key") } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 95e92e21b09..d70f994e87b 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -348,6 +348,9 @@ language_item_table! { I128ShroFnLangItem, "i128_shro", i128_shro_fn; U128ShroFnLangItem, "u128_shro", u128_shro_fn; + // Align offset for stride != 1, must not panic. + AlignOffsetLangItem, "align_offset", align_offset_fn; + TerminationTraitLangItem, "termination", termination; } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index b39311a7471..3db8c746713 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -117,6 +117,7 @@ use std::io::prelude::*; use std::io; use std::rc::Rc; use syntax::ast::{self, NodeId}; +use syntax::ptr::P; use syntax::symbol::keywords; use syntax_pos::Span; @@ -398,72 +399,65 @@ fn visit_fn<'a, 'tcx: 'a>(ir: &mut IrMaps<'a, 'tcx>, lsets.warn_about_unused_args(body, entry_ln); } -fn visit_local<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, local: &'tcx hir::Local) { - local.pat.each_binding(|_, p_id, sp, path1| { - debug!("adding local variable {}", p_id); +fn add_from_pat<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, pat: &P<hir::Pat>) { + // For struct patterns, take note of which fields used shorthand + // (`x` rather than `x: x`). + // + // FIXME: according to the rust-lang-nursery/rustc-guide book, `NodeId`s are to be + // phased out in favor of `HirId`s; however, we need to match the signature of + // `each_binding`, which uses `NodeIds`. + let mut shorthand_field_ids = NodeSet(); + let mut pats = VecDeque::new(); + pats.push_back(pat); + while let Some(pat) = pats.pop_front() { + use hir::PatKind::*; + match pat.node { + Binding(_, _, _, ref inner_pat) => { + pats.extend(inner_pat.iter()); + } + Struct(_, ref fields, _) => { + for field in fields { + if field.node.is_shorthand { + shorthand_field_ids.insert(field.node.pat.id); + } + } + } + Ref(ref inner_pat, _) | + Box(ref inner_pat) => { + pats.push_back(inner_pat); + } + TupleStruct(_, ref inner_pats, _) | + Tuple(ref inner_pats, _) => { + pats.extend(inner_pats.iter()); + } + Slice(ref pre_pats, ref inner_pat, ref post_pats) => { + pats.extend(pre_pats.iter()); + pats.extend(inner_pat.iter()); + pats.extend(post_pats.iter()); + } + _ => {} + } + } + + pat.each_binding(|_bm, p_id, _sp, path1| { let name = path1.node; - ir.add_live_node_for_node(p_id, VarDefNode(sp)); + ir.add_live_node_for_node(p_id, VarDefNode(path1.span)); ir.add_variable(Local(LocalInfo { id: p_id, name, - is_shorthand: false, + is_shorthand: shorthand_field_ids.contains(&p_id) })); }); +} + +fn visit_local<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, local: &'tcx hir::Local) { + add_from_pat(ir, &local.pat); intravisit::walk_local(ir, local); } fn visit_arm<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, arm: &'tcx hir::Arm) { - for mut pat in &arm.pats { - // For struct patterns, take note of which fields used shorthand - // (`x` rather than `x: x`). - // - // FIXME: according to the rust-lang-nursery/rustc-guide book, `NodeId`s are to be - // phased out in favor of `HirId`s; however, we need to match the signature of - // `each_binding`, which uses `NodeIds`. - let mut shorthand_field_ids = NodeSet(); - let mut pats = VecDeque::new(); - pats.push_back(pat); - while let Some(pat) = pats.pop_front() { - use hir::PatKind::*; - match pat.node { - Binding(_, _, _, ref inner_pat) => { - pats.extend(inner_pat.iter()); - } - Struct(_, ref fields, _) => { - for field in fields { - if field.node.is_shorthand { - shorthand_field_ids.insert(field.node.pat.id); - } - } - } - Ref(ref inner_pat, _) | - Box(ref inner_pat) => { - pats.push_back(inner_pat); - } - TupleStruct(_, ref inner_pats, _) | - Tuple(ref inner_pats, _) => { - pats.extend(inner_pats.iter()); - } - Slice(ref pre_pats, ref inner_pat, ref post_pats) => { - pats.extend(pre_pats.iter()); - pats.extend(inner_pat.iter()); - pats.extend(post_pats.iter()); - } - _ => {} - } - } - - pat.each_binding(|bm, p_id, _sp, path1| { - debug!("adding local variable {} from match with bm {:?}", - p_id, bm); - let name = path1.node; - ir.add_live_node_for_node(p_id, VarDefNode(path1.span)); - ir.add_variable(Local(LocalInfo { - id: p_id, - name: name, - is_shorthand: shorthand_field_ids.contains(&p_id) - })); - }) + for pat in &arm.pats { + add_from_pat(ir, pat); } intravisit::walk_arm(ir, arm); } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 71d7abed6c9..42a08afe305 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -542,18 +542,6 @@ impl<'tcx> ScopeTree { assert!(previous.is_none()); } - fn closure_is_enclosed_by(&self, - mut sub_closure: hir::ItemLocalId, - sup_closure: hir::ItemLocalId) -> bool { - loop { - if sub_closure == sup_closure { return true; } - match self.closure_tree.get(&sub_closure) { - Some(&s) => { sub_closure = s; } - None => { return false; } - } - } - } - fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); assert!(var != lifetime.item_local_id()); @@ -688,65 +676,37 @@ impl<'tcx> ScopeTree { // requires a hash table lookup, and we often have very long scope // chains (10s or 100s of scopes) that only differ by a few elements at // the start. So this algorithm is faster. - let mut ma = Some(scope_a); - let mut mb = Some(scope_b); - let mut seen_a: SmallVec<[Scope; 32]> = SmallVec::new(); - let mut seen_b: SmallVec<[Scope; 32]> = SmallVec::new(); + + let mut ma = Some(&scope_a); + let mut mb = Some(&scope_b); + + // A HashSet<Scope> is a more obvious choice for these, but SmallVec is + // faster because the set size is normally small so linear search is + // as good or better than a hash table lookup, plus the size is usually + // small enough to avoid a heap allocation. + let mut seen_a: SmallVec<[&Scope; 32]> = SmallVec::new(); + let mut seen_b: SmallVec<[&Scope; 32]> = SmallVec::new(); + loop { if let Some(a) = ma { - if seen_b.iter().position(|s| *s == a).is_some() { - return a; + if seen_b.iter().any(|s| *s == a) { + return *a; } seen_a.push(a); - ma = self.parent_map.get(&a).map(|s| *s); + ma = self.parent_map.get(&a); } if let Some(b) = mb { - if seen_a.iter().position(|s| *s == b).is_some() { - return b; + if seen_a.iter().any(|s| *s == b) { + return *b; } seen_b.push(b); - mb = self.parent_map.get(&b).map(|s| *s); + mb = self.parent_map.get(&b); } if ma.is_none() && mb.is_none() { - break; - } - }; - - fn outermost_scope(parent_map: &FxHashMap<Scope, Scope>, scope: Scope) -> Scope { - let mut scope = scope; - loop { - match parent_map.get(&scope) { - Some(&superscope) => scope = superscope, - None => break scope, - } - } - } - - // In this (rare) case, the two regions belong to completely different - // functions. Compare those fn for lexical nesting. The reasoning - // behind this is subtle. See the "Modeling closures" section of the - // README in infer::region_constraints for more details. - let a_root_scope = outermost_scope(&self.parent_map, scope_a); - let b_root_scope = outermost_scope(&self.parent_map, scope_b); - match (a_root_scope.data(), b_root_scope.data()) { - (ScopeData::Destruction(a_root_id), - ScopeData::Destruction(b_root_id)) => { - if self.closure_is_enclosed_by(a_root_id, b_root_id) { - // `a` is enclosed by `b`, hence `b` is the ancestor of everything in `a` - scope_b - } else if self.closure_is_enclosed_by(b_root_id, a_root_id) { - // `b` is enclosed by `a`, hence `a` is the ancestor of everything in `b` - scope_a - } else { - // neither fn encloses the other - bug!() - } - } - _ => { - // root ids are always Node right now - bug!() + // No nearest common ancestor found. + bug!(); } } } diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 1e1d50c3fc0..6885bf89cc8 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -2,6 +2,7 @@ use std::{fmt, env}; use mir; use ty::{FnSig, Ty, layout}; +use ty::layout::{Size, Align}; use super::{ MemoryPointer, Lock, AccessKind @@ -47,7 +48,7 @@ pub enum EvalErrorKind<'tcx, O> { PointerOutOfBounds { ptr: MemoryPointer, access: bool, - allocation_size: u64, + allocation_size: Size, }, InvalidNullPointerUsage, ReadPointerAsBytes, @@ -71,8 +72,8 @@ pub enum EvalErrorKind<'tcx, O> { TlsOutOfBounds, AbiViolation(String), AlignmentCheckFailed { - required: u64, - has: u64, + required: Align, + has: Align, }, MemoryLockViolation { ptr: MemoryPointer, @@ -108,7 +109,7 @@ pub enum EvalErrorKind<'tcx, O> { DeallocatedWrongMemoryKind(String, String), ReallocateNonBasePtr, DeallocateNonBasePtr, - IncorrectAllocationInformation(u64, usize, u64, u64), + IncorrectAllocationInformation(Size, Size, Align, Align), Layout(layout::LayoutError<'tcx>), HeapAllocZeroBytes, HeapAllocNonPowerOfTwoAlignment(u64), @@ -269,7 +270,7 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> { PointerOutOfBounds { ptr, access, allocation_size } => { write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}", if access { "memory access" } else { "pointer computed" }, - ptr.offset, ptr.alloc_id, allocation_size) + ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes()) }, MemoryLockViolation { ptr, len, frame, access, ref lock } => { write!(f, "{:?} access by frame {} at {:?}, size {}, is in conflict with lock {:?}", @@ -305,7 +306,7 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> { write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c), AlignmentCheckFailed { required, has } => write!(f, "tried to access memory with alignment {}, but alignment {} is required", - has, required), + has.abi(), required.abi()), TypeNotPrimitive(ty) => write!(f, "expected primitive type, got {}", ty), Layout(ref err) => @@ -315,7 +316,7 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> { MachineError(ref inner) => write!(f, "{}", inner), IncorrectAllocationInformation(size, size2, align, align2) => - write!(f, "incorrect alloc info: expected size {} and align {}, got size {} and align {}", size, align, size2, align2), + write!(f, "incorrect alloc info: expected size {} and align {}, got size {} and align {}", size.bytes(), align.abi(), size2.bytes(), align2.abi()), _ => write!(f, "{}", self.description()), } } diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 31885c1e020..d4f18acf8ad 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -17,7 +17,7 @@ use std::fmt; use mir; use hir::def_id::DefId; use ty::{self, TyCtxt}; -use ty::layout::{self, Align, HasDataLayout}; +use ty::layout::{self, Align, HasDataLayout, Size}; use middle::region; use std::iter; use std::io; @@ -109,42 +109,42 @@ impl<T: layout::HasDataLayout> PointerArithmetic for T {} #[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub struct MemoryPointer { pub alloc_id: AllocId, - pub offset: u64, + pub offset: Size, } impl<'tcx> MemoryPointer { - pub fn new(alloc_id: AllocId, offset: u64) -> Self { + pub fn new(alloc_id: AllocId, offset: Size) -> Self { MemoryPointer { alloc_id, offset } } pub(crate) fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self { MemoryPointer::new( self.alloc_id, - cx.data_layout().wrapping_signed_offset(self.offset, i), + Size::from_bytes(cx.data_layout().wrapping_signed_offset(self.offset.bytes(), i)), ) } pub fn overflowing_signed_offset<C: HasDataLayout>(self, i: i128, cx: C) -> (Self, bool) { - let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset, i); - (MemoryPointer::new(self.alloc_id, res), over) + let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i); + (MemoryPointer::new(self.alloc_id, Size::from_bytes(res)), over) } pub(crate) fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> { Ok(MemoryPointer::new( self.alloc_id, - cx.data_layout().signed_offset(self.offset, i)?, + Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?), )) } - pub fn overflowing_offset<C: HasDataLayout>(self, i: u64, cx: C) -> (Self, bool) { - let (res, over) = cx.data_layout().overflowing_offset(self.offset, i); - (MemoryPointer::new(self.alloc_id, res), over) + pub fn overflowing_offset<C: HasDataLayout>(self, i: Size, cx: C) -> (Self, bool) { + let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes()); + (MemoryPointer::new(self.alloc_id, Size::from_bytes(res)), over) } - pub fn offset<C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> { + pub fn offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> { Ok(MemoryPointer::new( self.alloc_id, - cx.data_layout().offset(self.offset, i)?, + Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?), )) } } @@ -244,7 +244,7 @@ pub struct Allocation { pub bytes: Vec<u8>, /// Maps from byte addresses to allocations. /// Only the first byte of a pointer is inserted into the map. - pub relocations: BTreeMap<u64, AllocId>, + pub relocations: BTreeMap<Size, AllocId>, /// Denotes undefined memory. Reading from undefined memory is forbidden in miri pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. @@ -257,8 +257,8 @@ pub struct Allocation { impl Allocation { pub fn from_bytes(slice: &[u8], align: Align) -> Self { - let mut undef_mask = UndefMask::new(0); - undef_mask.grow(slice.len() as u64, true); + let mut undef_mask = UndefMask::new(Size::from_bytes(0)); + undef_mask.grow(Size::from_bytes(slice.len() as u64), true); Self { bytes: slice.to_owned(), relocations: BTreeMap::new(), @@ -272,10 +272,10 @@ impl Allocation { Allocation::from_bytes(slice, Align::from_bytes(1, 1).unwrap()) } - pub fn undef(size: u64, align: Align) -> Self { - assert_eq!(size as usize as u64, size); + pub fn undef(size: Size, align: Align) -> Self { + assert_eq!(size.bytes() as usize as u64, size.bytes()); Allocation { - bytes: vec![0; size as usize], + bytes: vec![0; size.bytes() as usize], relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align, @@ -331,35 +331,35 @@ const BLOCK_SIZE: u64 = 64; #[derive(Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct UndefMask { blocks: Vec<Block>, - len: u64, + len: Size, } impl_stable_hash_for!(struct mir::interpret::UndefMask{blocks, len}); impl UndefMask { - pub fn new(size: u64) -> Self { + pub fn new(size: Size) -> Self { let mut m = UndefMask { blocks: vec![], - len: 0, + len: Size::from_bytes(0), }; m.grow(size, false); m } /// Check whether the range `start..end` (end-exclusive) is entirely defined. - pub fn is_range_defined(&self, start: u64, end: u64) -> bool { + pub fn is_range_defined(&self, start: Size, end: Size) -> bool { if end > self.len { return false; } - for i in start..end { - if !self.get(i) { + for i in start.bytes()..end.bytes() { + if !self.get(Size::from_bytes(i)) { return false; } } true } - pub fn set_range(&mut self, start: u64, end: u64, new_state: bool) { + pub fn set_range(&mut self, start: Size, end: Size, new_state: bool) { let len = self.len; if end > len { self.grow(end - len, new_state); @@ -367,18 +367,18 @@ impl UndefMask { self.set_range_inbounds(start, end, new_state); } - pub fn set_range_inbounds(&mut self, start: u64, end: u64, new_state: bool) { - for i in start..end { - self.set(i, new_state); + pub fn set_range_inbounds(&mut self, start: Size, end: Size, new_state: bool) { + for i in start.bytes()..end.bytes() { + self.set(Size::from_bytes(i), new_state); } } - pub fn get(&self, i: u64) -> bool { + pub fn get(&self, i: Size) -> bool { let (block, bit) = bit_index(i); (self.blocks[block] & 1 << bit) != 0 } - pub fn set(&mut self, i: u64, new_state: bool) { + pub fn set(&mut self, i: Size, new_state: bool) { let (block, bit) = bit_index(i); if new_state { self.blocks[block] |= 1 << bit; @@ -387,10 +387,10 @@ impl UndefMask { } } - pub fn grow(&mut self, amount: u64, new_state: bool) { - let unused_trailing_bits = self.blocks.len() as u64 * BLOCK_SIZE - self.len; - if amount > unused_trailing_bits { - let additional_blocks = amount / BLOCK_SIZE + 1; + pub fn grow(&mut self, amount: Size, new_state: bool) { + let unused_trailing_bits = self.blocks.len() as u64 * BLOCK_SIZE - self.len.bytes(); + if amount.bytes() > unused_trailing_bits { + let additional_blocks = amount.bytes() / BLOCK_SIZE + 1; assert_eq!(additional_blocks as usize as u64, additional_blocks); self.blocks.extend( iter::repeat(0).take(additional_blocks as usize), @@ -402,7 +402,8 @@ impl UndefMask { } } -fn bit_index(bits: u64) -> (usize, usize) { +fn bit_index(bits: Size) -> (usize, usize) { + let bits = bits.bytes(); let a = bits / BLOCK_SIZE; let b = bits % BLOCK_SIZE; assert_eq!(a as usize as u64, a); diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 7bd84a607e7..5ac2f7f356e 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -1,6 +1,6 @@ #![allow(unknown_lints)] -use ty::layout::{Align, HasDataLayout}; +use ty::layout::{Align, HasDataLayout, Size}; use ty; use super::{EvalResult, MemoryPointer, PointerArithmetic, Allocation}; @@ -9,12 +9,12 @@ use super::{EvalResult, MemoryPointer, PointerArithmetic, Allocation}; /// matches Value's optimizations for easy conversions between these two types #[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub enum ConstValue<'tcx> { - // Used only for types with layout::abi::Scalar ABI and ZSTs which use PrimVal::Undef + /// Used only for types with layout::abi::Scalar ABI and ZSTs which use PrimVal::Undef ByVal(PrimVal), - // Used only for types with layout::abi::ScalarPair + /// Used only for types with layout::abi::ScalarPair ByValPair(PrimVal, PrimVal), - // Used only for the remaining cases - ByRef(&'tcx Allocation), + /// Used only for the remaining cases. An allocation + offset into the allocation + ByRef(&'tcx Allocation, Size), } impl<'tcx> ConstValue<'tcx> { @@ -129,13 +129,13 @@ impl<'tcx> Pointer { } } - pub fn offset<C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> { + pub fn offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> { let layout = cx.data_layout(); match self.primval { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); Ok(Pointer::from( - PrimVal::Bytes(layout.offset(b as u64, i)? as u128), + PrimVal::Bytes(layout.offset(b as u64, i.bytes())? as u128), )) } PrimVal::Ptr(ptr) => ptr.offset(i, layout).map(Pointer::from), @@ -336,25 +336,25 @@ impl PrimValKind { } } - pub fn from_uint_size(size: u64) -> Self { - match size { + pub fn from_uint_size(size: Size) -> Self { + match size.bytes() { 1 => PrimValKind::U8, 2 => PrimValKind::U16, 4 => PrimValKind::U32, 8 => PrimValKind::U64, 16 => PrimValKind::U128, - _ => bug!("can't make uint with size {}", size), + _ => bug!("can't make uint with size {}", size.bytes()), } } - pub fn from_int_size(size: u64) -> Self { - match size { + pub fn from_int_size(size: Size) -> Self { + match size.bytes() { 1 => PrimValKind::I8, 2 => PrimValKind::I16, 4 => PrimValKind::I32, 8 => PrimValKind::I64, 16 => PrimValKind::I128, - _ => bug!("can't make int with size {}", size), + _ => bug!("can't make int with size {}", size.bytes()), } } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 40e9b687f0c..f42f876510d 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1913,7 +1913,7 @@ pub fn print_miri_value<W: Write>(value: Value, ty: Ty, f: &mut W) -> fmt::Resul .get_alloc(ptr.alloc_id); if let Some(alloc) = alloc { assert_eq!(len as usize as u128, len); - let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)]; + let slice = &alloc.bytes[(ptr.offset.bytes() as usize)..][..(len as usize)]; let s = ::std::str::from_utf8(slice) .expect("non utf8 str from miri"); write!(f, "{:?}", s) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 2344cd11f66..bcad3fbc841 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1239,8 +1239,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print the result of the monomorphization collection pass"), mir_opt_level: usize = (1, parse_uint, [TRACKED], "set the MIR optimization level (0-3, default: 1)"), - mutable_noalias: bool = (false, parse_bool, [TRACKED], - "emit noalias metadata for mutable references"), + mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED], + "emit noalias metadata for mutable references (default: yes on LLVM >= 6)"), arg_align_attributes: bool = (false, parse_bool, [TRACKED], "emit align metadata for reference arguments"), dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED], diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index e4520bce681..97ce730c59e 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -203,17 +203,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { obligation.cause.span, infer::LateBoundRegionConversionTime::HigherRankedType, data); - let normalized = super::normalize_projection_type( + let mut obligations = vec![]; + let normalized_ty = super::normalize_projection_type( &mut selcx, obligation.param_env, data.projection_ty, obligation.cause.clone(), - 0 + 0, + &mut obligations ); if let Err(error) = self.at(&obligation.cause, obligation.param_env) - .eq(normalized.value, data.ty) { + .eq(normalized_ty, data.ty) { values = Some(infer::ValuePairs::Types(ExpectedFound { - expected: normalized.value, + expected: normalized_ty, found: data.ty, })); err_buf = error; diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 94ee3947077..4447a2b6ed1 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -161,19 +161,18 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { // FIXME(#20304) -- cache let mut selcx = SelectionContext::new(infcx); - let normalized = project::normalize_projection_type(&mut selcx, - param_env, - projection_ty, - cause, - 0); - - for obligation in normalized.obligations { - self.register_predicate_obligation(infcx, obligation); - } - - debug!("normalize_projection_type: result={:?}", normalized.value); - - normalized.value + let mut obligations = vec![]; + let normalized_ty = project::normalize_projection_type(&mut selcx, + param_env, + projection_ty, + cause, + 0, + &mut obligations); + self.register_predicate_obligations(infcx, obligations); + + debug!("normalize_projection_type: result={:?}", normalized_ty); + + normalized_ty } /// Requires that `ty` must implement the trait with `def_id` in diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 270e06a872b..174c35d1d69 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -225,12 +225,14 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>( debug!("project_and_unify_type(obligation={:?})", obligation); - let Normalized { value: normalized_ty, mut obligations } = + let mut obligations = vec![]; + let normalized_ty = match opt_normalize_projection_type(selcx, obligation.param_env, obligation.predicate.projection_ty, obligation.cause.clone(), - obligation.recursion_depth) { + obligation.recursion_depth, + &mut obligations) { Some(n) => n, None => return Ok(None), }; @@ -386,16 +388,15 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, // binder). It would be better to normalize in a // binding-aware fashion. - let Normalized { value: normalized_ty, obligations } = - normalize_projection_type(self.selcx, - self.param_env, - data.clone(), - self.cause.clone(), - self.depth); - debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \ - with {} add'l obligations", - self.depth, ty, normalized_ty, obligations.len()); - self.obligations.extend(obligations); + let normalized_ty = normalize_projection_type(self.selcx, + self.param_env, + data.clone(), + self.cause.clone(), + self.depth, + &mut self.obligations); + debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?}, \ + now with {} obligations", + self.depth, ty, normalized_ty, self.obligations.len()); normalized_ty } @@ -471,10 +472,12 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>( param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, - depth: usize) - -> NormalizedTy<'tcx> + depth: usize, + obligations: &mut Vec<PredicateObligation<'tcx>>) + -> Ty<'tcx> { - opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth) + opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth, + obligations) .unwrap_or_else(move || { // if we bottom out in ambiguity, create a type variable // and a deferred predicate to resolve this when more type @@ -490,10 +493,8 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>( }); let obligation = Obligation::with_depth( cause, depth + 1, param_env, projection.to_predicate()); - Normalized { - value: ty_var, - obligations: vec![obligation] - } + obligations.push(obligation); + ty_var }) } @@ -501,13 +502,20 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>( /// as Trait>::Item`. The result is always a type (and possibly /// additional obligations). Returns `None` in the case of ambiguity, /// which indicates that there are unbound type variables. +/// +/// This function used to return `Option<NormalizedTy<'tcx>>`, which contains a +/// `Ty<'tcx>` and an obligations vector. But that obligation vector was very +/// often immediately appended to another obligations vector. So now this +/// function takes an obligations vector and appends to it directly, which is +/// slightly uglier but avoids the need for an extra short-lived allocation. fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, - depth: usize) - -> Option<NormalizedTy<'tcx>> + depth: usize, + obligations: &mut Vec<PredicateObligation<'tcx>>) + -> Option<Ty<'tcx>> { let infcx = selcx.infcx(); @@ -579,7 +587,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( projection_ty); selcx.infcx().report_overflow_error(&obligation, false); } - Err(ProjectionCacheEntry::NormalizedTy(mut ty)) => { + Err(ProjectionCacheEntry::NormalizedTy(ty)) => { + // This is the hottest path in this function. + // // If we find the value in the cache, then return it along // with the obligations that went along with it. Note // that, when using a fulfillment context, these @@ -596,29 +606,32 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( // Once we have inferred everything we need to know, we // can ignore the `obligations` from that point on. if !infcx.any_unresolved_type_vars(&ty.value) { - infcx.projection_cache.borrow_mut().complete(cache_key); - ty.obligations = vec![]; + infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty); + // No need to extend `obligations`. + } else { + obligations.extend(ty.obligations); } - push_paranoid_cache_value_obligation(infcx, - param_env, - projection_ty, - cause, - depth, - &mut ty); - - return Some(ty); + obligations.push(get_paranoid_cache_value_obligation(infcx, + param_env, + projection_ty, + cause, + depth)); + return Some(ty.value); } Err(ProjectionCacheEntry::Error) => { debug!("opt_normalize_projection_type: \ found error"); - return Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth)); + let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); + obligations.extend(result.obligations); + return Some(result.value) } } let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty); match project_type(selcx, &obligation) { - Ok(ProjectedTy::Progress(Progress { ty: projected_ty, mut obligations })) => { + Ok(ProjectedTy::Progress(Progress { ty: projected_ty, + obligations: mut projected_obligations })) => { // if projection succeeded, then what we get out of this // is also non-normalized (consider: it was derived from // an impl, where-clause etc) and hence we must @@ -627,10 +640,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( debug!("opt_normalize_projection_type: \ projected_ty={:?} \ depth={} \ - obligations={:?}", + projected_obligations={:?}", projected_ty, depth, - obligations); + projected_obligations); let result = if projected_ty.has_projections() { let mut normalizer = AssociatedTypeNormalizer::new(selcx, @@ -644,22 +657,22 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( normalized_ty, depth); - obligations.extend(normalizer.obligations); + projected_obligations.extend(normalizer.obligations); Normalized { value: normalized_ty, - obligations, + obligations: projected_obligations, } } else { Normalized { value: projected_ty, - obligations, + obligations: projected_obligations, } }; let cache_value = prune_cache_value_obligations(infcx, &result); infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value); - - Some(result) + obligations.extend(result.obligations); + Some(result.value) } Ok(ProjectedTy::NoProgress(projected_ty)) => { debug!("opt_normalize_projection_type: \ @@ -670,7 +683,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( obligations: vec![] }; infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone()); - Some(result) + // No need to extend `obligations`. + Some(result.value) } Err(ProjectionTyError::TooManyCandidates) => { debug!("opt_normalize_projection_type: \ @@ -688,7 +702,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( infcx.projection_cache.borrow_mut() .error(cache_key); - Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth)) + let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); + obligations.extend(result.obligations); + Some(result.value) } } } @@ -737,7 +753,7 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, /// may or may not be necessary -- in principle, all the obligations /// that must be proven to show that `T: Trait` were also returned /// when the cache was first populated. But there are some vague concerns, -/// and so we take the precatuionary measure of including `T: Trait` in +/// and so we take the precautionary measure of including `T: Trait` in /// the result: /// /// Concern #1. The current setup is fragile. Perhaps someone could @@ -754,19 +770,21 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, /// that may yet turn out to be wrong. This *may* lead to some sort /// of trouble, though we don't have a concrete example of how that /// can occur yet. But it seems risky at best. -fn push_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - result: &mut NormalizedTy<'tcx>) +fn get_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>( + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize) + -> PredicateObligation<'tcx> { let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref(); - let trait_obligation = Obligation { cause, - recursion_depth: depth, - param_env, - predicate: trait_ref.to_predicate() }; - result.obligations.push(trait_obligation); + Obligation { + cause, + recursion_depth: depth, + param_env, + predicate: trait_ref.to_predicate(), + } } /// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not @@ -1682,6 +1700,23 @@ impl<'tcx> ProjectionCache<'tcx> { })); } + /// A specialized version of `complete` for when the key's value is known + /// to be a NormalizedTy. + pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &NormalizedTy<'tcx>) { + // We want to insert `ty` with no obligations. If the existing value + // already has no obligations (as is common) we can use `insert_noop` + // to do a minimal amount of work -- the HashMap insertion is skipped, + // and minimal changes are made to the undo log. + if ty.obligations.is_empty() { + self.map.insert_noop(); + } else { + self.map.insert(key, ProjectionCacheEntry::NormalizedTy(Normalized { + value: ty.value, + obligations: vec![] + })); + } + } + /// Indicates that trying to normalize `key` resulted in /// ambiguity. No point in trying it again then until we gain more /// type information (in which case, the "fully resolved" key will diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 4493c668593..0b8f10d311d 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -19,7 +19,7 @@ use ty::subst::{Substs, Subst, Kind, UnpackedKind}; use ty::{self, AdtDef, TypeFlags, Ty, TyCtxt, TypeFoldable}; use ty::{Slice, TyS}; use util::captures::Captures; -use mir::interpret::{Allocation, PrimVal, MemoryPointer, Value, ConstValue}; +use mir::interpret::{PrimVal, MemoryPointer, Value, ConstValue}; use std::iter; use std::cmp::Ordering; @@ -1768,15 +1768,6 @@ impl<'tcx> Const<'tcx> { } #[inline] - pub fn from_alloc( - tcx: TyCtxt<'_, '_, 'tcx>, - alloc: &'tcx Allocation, - ty: Ty<'tcx>, - ) -> &'tcx Self { - Self::from_const_value(tcx, ConstValue::ByRef(alloc), ty) - } - - #[inline] pub fn from_byval_value( tcx: TyCtxt<'_, '_, 'tcx>, val: Value, diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index d5532f8f835..7cfcbcb86af 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -15,10 +15,8 @@ use hir::def_id::DefId; use hir::map::{DefPathData, Node}; use hir; use ich::NodeIdHashingMode; -use middle::const_val::ConstVal; use traits::{self, ObligationCause}; use ty::{self, Ty, TyCtxt, GenericParamDefKind, TypeFoldable}; -use ty::fold::TypeVisitor; use ty::subst::{Substs, UnpackedKind}; use ty::maps::TyCtxtAt; use ty::TypeVariants::*; @@ -26,12 +24,9 @@ use ty::layout::{Integer, IntegerExt}; use util::common::ErrorReported; use middle::lang_items; -use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, - HashStable}; +use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use rustc_data_structures::fx::FxHashMap; use std::{cmp, fmt}; -use std::hash::Hash; -use std::intrinsics; use syntax::ast; use syntax::attr::{self, SignedInt, UnsignedInt}; use syntax_pos::{Span, DUMMY_SP}; @@ -615,150 +610,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } -pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, W> { - tcx: TyCtxt<'a, 'gcx, 'tcx>, - state: StableHasher<W>, -} - -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() } - } - - pub fn finish(self) -> W { - self.state.finish() - } - - pub fn hash<T: Hash>(&mut self, x: T) { - x.hash(&mut self.state); - } - - fn hash_discriminant_u8<T>(&mut self, x: &T) { - let v = unsafe { - intrinsics::discriminant_value(x) - }; - let b = v as u8; - assert_eq!(v, b as u64); - self.hash(b) - } - - fn def_id(&mut self, did: DefId) { - // Hash the DefPath corresponding to the DefId, which is independent - // of compiler internal state. We already have a stable hash value of - // all DefPaths available via tcx.def_path_hash(), so we just feed that - // into the hasher. - let hash = self.tcx.def_path_hash(did); - self.hash(hash); - } -} - -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); - - match ty.sty { - TyInt(i) => self.hash(i), - TyUint(u) => self.hash(u), - TyFloat(f) => self.hash(f), - TyArray(_, n) => { - self.hash_discriminant_u8(&n.val); - match n.val { - ConstVal::Value(alloc) => self.hash(alloc), - ConstVal::Unevaluated(def_id, _) => self.def_id(def_id), - } - } - TyRawPtr(m) => self.hash(m.mutbl), - TyRef(_, _, mutbl) => self.hash(mutbl), - TyClosure(def_id, _) | - TyGenerator(def_id, _, _) | - TyAnon(def_id, _) | - TyFnDef(def_id, _) => self.def_id(def_id), - TyAdt(d, _) => self.def_id(d.did), - TyForeign(def_id) => self.def_id(def_id), - TyFnPtr(f) => { - self.hash(f.unsafety()); - self.hash(f.abi()); - self.hash(f.variadic()); - self.hash(f.inputs().skip_binder().len()); - } - TyDynamic(ref data, ..) => { - if let Some(p) = data.principal() { - self.def_id(p.def_id()); - } - for d in data.auto_traits() { - self.def_id(d); - } - } - TyGeneratorWitness(tys) => { - self.hash(tys.skip_binder().len()); - } - TyTuple(tys) => { - self.hash(tys.len()); - } - TyParam(p) => { - self.hash(p.idx); - self.hash(p.name); - } - TyProjection(ref data) => { - self.def_id(data.item_def_id); - } - TyNever | - TyBool | - TyChar | - TyStr | - TySlice(_) => {} - - TyError | - TyInfer(_) => bug!("TypeIdHasher: unexpected type {}", ty) - } - - ty.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - self.hash_discriminant_u8(r); - match *r { - ty::ReErased | - ty::ReStatic | - ty::ReEmpty => { - // No variant fields to hash for these ... - } - ty::ReCanonical(c) => { - self.hash(c); - } - ty::ReLateBound(db, ty::BrAnon(i)) => { - self.hash(db.depth); - self.hash(i); - } - ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => { - self.def_id(def_id); - } - - ty::ReClosureBound(..) | - ty::ReLateBound(..) | - ty::ReFree(..) | - ty::ReScope(..) | - ty::ReVar(..) | - ty::ReSkolemized(..) => { - bug!("TypeIdHasher: unexpected region {:?}", r) - } - } - false - } - - fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, x: &ty::Binder<T>) -> bool { - // Anonymize late-bound regions so that, for example: - // `for<'a, b> fn(&'a &'b T)` and `for<'a, b> fn(&'b &'a T)` - // result in the same TypeId (the two types are equivalent). - self.tcx.anonymize_late_bound_regions(x).super_visit_with(self) - } -} - impl<'a, 'tcx> ty::TyS<'tcx> { pub fn moves_by_default(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index de8814d3d6a..497d5fdcac7 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -21,7 +21,7 @@ use syntax::ext::base::ExtCtxt; use syntax::ext::base::Resolver; use syntax::ext::build::AstBuilder; use syntax::ext::expand::ExpansionConfig; -use syntax::ext::hygiene::{Mark, SyntaxContext}; +use syntax::ext::hygiene::{self, Mark, SyntaxContext}; use syntax::fold::{self, Folder}; use syntax::parse::ParseSess; use syntax::ptr::P; @@ -86,6 +86,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> { span: None, allow_internal_unstable: true, allow_internal_unsafe: false, + edition: hygiene::default_edition(), }, }); let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(mark)); diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs index 25c598c532c..221012903d9 100644 --- a/src/librustc_codegen_llvm/abi.rs +++ b/src/librustc_codegen_llvm/abi.rs @@ -127,8 +127,12 @@ impl LlvmType for Reg { impl LlvmType for CastTarget { fn llvm_type(&self, cx: &CodegenCx) -> Type { let rest_ll_unit = self.rest.unit.llvm_type(cx); - let rest_count = self.rest.total.bytes() / self.rest.unit.size.bytes(); - let rem_bytes = self.rest.total.bytes() % self.rest.unit.size.bytes(); + let (rest_count, rem_bytes) = if self.rest.unit.size.bytes() == 0 { + (0, 0) + } else { + (self.rest.total.bytes() / self.rest.unit.size.bytes(), + self.rest.total.bytes() % self.rest.unit.size.bytes()) + }; if self.prefix.iter().all(|x| x.is_none()) { // Simplify to a single unit when there is no prefix and size <= unit size diff --git a/src/librustc_codegen_llvm/back/link.rs b/src/librustc_codegen_llvm/back/link.rs index dbfd430a3e2..735c4d2f76f 100644 --- a/src/librustc_codegen_llvm/back/link.rs +++ b/src/librustc_codegen_llvm/back/link.rs @@ -625,11 +625,6 @@ fn link_natively(sess: &Session, if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { cmd.args(args); } - if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) { - if sess.crt_static() { - cmd.args(args); - } - } if let Some(ref args) = sess.opts.debugging_opts.pre_link_args { cmd.args(args); } @@ -644,18 +639,6 @@ fn link_natively(sess: &Session, cmd.arg(root.join(obj)); } - if crate_type == config::CrateTypeExecutable && sess.crt_static() { - for obj in &sess.target.target.options.pre_link_objects_exe_crt { - cmd.arg(root.join(obj)); - } - - for obj in &sess.target.target.options.pre_link_objects_exe_crt_sys { - if flavor == LinkerFlavor::Gcc { - cmd.arg(format!("-l:{}", obj)); - } - } - } - if sess.target.target.options.is_like_emscripten { cmd.arg("-s"); cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { @@ -677,16 +660,6 @@ fn link_natively(sess: &Session, for obj in &sess.target.target.options.post_link_objects { cmd.arg(root.join(obj)); } - if sess.crt_static() { - for obj in &sess.target.target.options.post_link_objects_crt_sys { - if flavor == LinkerFlavor::Gcc { - cmd.arg(format!("-l:{}", obj)); - } - } - for obj in &sess.target.target.options.post_link_objects_crt { - cmd.arg(root.join(obj)); - } - } if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { cmd.args(args); } diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index ee60711c11d..ae0f6067f47 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -23,12 +23,11 @@ use llvm::{self, ValueRef}; use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType, DILexicalBlock, DIFlags}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc::hir::CodegenFnAttrFlags; use rustc::hir::def::CtorKind; use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; -use rustc::ty::fold::TypeVisitor; -use rustc::ty::util::TypeIdHasher; -use rustc::ich::Fingerprint; +use rustc::ich::{Fingerprint, NodeIdHashingMode}; use rustc::ty::Instance; use common::CodegenCx; use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; @@ -144,9 +143,15 @@ 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. - let mut type_id_hasher = TypeIdHasher::<Fingerprint>::new(cx.tcx); - type_id_hasher.visit_ty(type_); - let unique_type_id = type_id_hasher.finish().to_hex(); + let mut hasher = StableHasher::<Fingerprint>::new(); + let mut hcx = cx.tcx.create_stable_hashing_context(); + let type_ = cx.tcx.erase_regions(&type_); + hcx.while_hashing_spans(false, |hcx| { + hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + type_.hash_stable(hcx, &mut hasher); + }); + }); + let unique_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)); diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index ba04cc7fad5..cffe7f79e97 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -389,18 +389,6 @@ pub fn codegen_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>, args[0].deref(bx.cx).codegen_get_discr(bx, ret_ty) } - "align_offset" => { - // `ptr as usize` - let ptr_val = bx.ptrtoint(args[0].immediate(), bx.cx.isize_ty); - // `ptr_val % align` - let align = args[1].immediate(); - let offset = bx.urem(ptr_val, align); - let zero = C_null(bx.cx.isize_ty); - // `offset == 0` - let is_zero = bx.icmp(llvm::IntPredicate::IntEQ, offset, zero); - // `if offset == 0 { 0 } else { align - offset }` - bx.select(is_zero, zero, bx.sub(align, offset)) - } name if name.starts_with("simd_") => { match generic_simd_intrinsic(bx, name, callee_ty, diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index bd053da4bd3..0b0bab96dfd 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -29,7 +29,6 @@ #![feature(rustc_diagnostic_macros)] #![feature(slice_sort_by_cached_key)] #![feature(optin_builtin_traits)] -#![feature(inclusive_range_methods)] use rustc::dep_graph::WorkProduct; use syntax_pos::symbol::Symbol; diff --git a/src/librustc_codegen_llvm/mir/constant.rs b/src/librustc_codegen_llvm/mir/constant.rs index c2638d2d410..36c1d335ec4 100644 --- a/src/librustc_codegen_llvm/mir/constant.rs +++ b/src/librustc_codegen_llvm/mir/constant.rs @@ -16,7 +16,7 @@ use rustc::mir; use rustc_data_structures::indexed_vec::Idx; use rustc::mir::interpret::{GlobalId, MemoryPointer, PrimVal, Allocation, ConstValue}; use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar}; +use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar, Size}; use builder::Builder; use common::{CodegenCx}; use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; @@ -68,7 +68,7 @@ pub fn primval_to_llvm(cx: &CodegenCx, let llval = unsafe { llvm::LLVMConstInBoundsGEP( consts::bitcast(base_addr, Type::i8p(cx)), - &C_usize(cx, ptr.offset), + &C_usize(cx, ptr.offset.bytes()), 1, ) }; if scalar.value != layout::Pointer { @@ -81,49 +81,6 @@ pub fn primval_to_llvm(cx: &CodegenCx, } } -fn const_value_to_llvm<'tcx>(cx: &CodegenCx<'_, 'tcx>, val: ConstValue, ty: Ty<'tcx>) -> ValueRef { - let layout = cx.layout_of(ty); - - if layout.is_zst() { - return C_undef(layout.immediate_llvm_type(cx)); - } - - match val { - ConstValue::ByVal(x) => { - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("const_value_to_llvm: invalid ByVal layout: {:#?}", layout) - }; - primval_to_llvm( - cx, - x, - scalar, - layout.immediate_llvm_type(cx), - ) - }, - ConstValue::ByValPair(a, b) => { - let (a_scalar, b_scalar) = match layout.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("const_value_to_llvm: invalid ByValPair layout: {:#?}", layout) - }; - let a_llval = primval_to_llvm( - cx, - a, - a_scalar, - layout.scalar_pair_element_llvm_type(cx, 0), - ); - let b_llval = primval_to_llvm( - cx, - b, - b_scalar, - layout.scalar_pair_element_llvm_type(cx, 1), - ); - C_struct(cx, &[a_llval, b_llval], false) - }, - ConstValue::ByRef(alloc) => const_alloc_to_llvm(cx, alloc), - } -} - pub fn const_alloc_to_llvm(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1); let layout = cx.data_layout(); @@ -131,6 +88,7 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { let mut next_offset = 0; for (&offset, &alloc_id) in &alloc.relocations { + let offset = offset.bytes(); assert_eq!(offset as usize as u64, offset); let offset = offset as usize; if offset > next_offset { @@ -142,7 +100,7 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { ).expect("const_alloc_to_llvm: could not read relocation pointer") as u64; llvals.push(primval_to_llvm( cx, - PrimVal::Ptr(MemoryPointer { alloc_id, offset: ptr_offset }), + PrimVal::Ptr(MemoryPointer { alloc_id, offset: Size::from_bytes(ptr_offset) }), &Scalar { value: layout::Primitive::Pointer, valid_range: 0..=!0 @@ -171,11 +129,11 @@ pub fn codegen_static_initializer<'a, 'tcx>( let param_env = ty::ParamEnv::reveal_all(); let static_ = cx.tcx.const_eval(param_env.and(cid))?; - let val = match static_.val { - ConstVal::Value(val) => val, + let alloc = match static_.val { + ConstVal::Value(ConstValue::ByRef(alloc, n)) if n.bytes() == 0 => alloc, _ => bug!("static const eval returned {:#?}", static_), }; - Ok(const_value_to_llvm(cx, val, static_.ty)) + Ok(const_alloc_to_llvm(cx, alloc)) } impl<'a, 'tcx> FunctionCx<'a, 'tcx> { diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs index 62ef58f8255..91e39373334 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::ValueRef; +use llvm::{ValueRef, LLVMConstInBoundsGEP}; use rustc::middle::const_val::ConstEvalErr; use rustc::mir; use rustc::mir::interpret::ConstValue; @@ -137,9 +137,15 @@ impl<'a, 'tcx> OperandRef<'tcx> { ); OperandValue::Pair(a_llval, b_llval) }, - ConstValue::ByRef(alloc) => { + ConstValue::ByRef(alloc, offset) => { let init = const_alloc_to_llvm(bx.cx, alloc); - let llval = consts::addr_of(bx.cx, init, layout.align, "byte_str"); + let base_addr = consts::addr_of(bx.cx, init, layout.align, "byte_str"); + + let llval = unsafe { LLVMConstInBoundsGEP( + consts::bitcast(base_addr, Type::i8p(bx.cx)), + &C_usize(bx.cx, offset.bytes()), + 1, + )}; let llval = consts::bitcast(llval, layout.llvm_type(bx.cx).ptr_to()); return Ok(PlaceRef::new_sized(llval, layout, alloc.align).load(bx)); }, diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index 5f186e7514e..21436b74731 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs @@ -10,6 +10,7 @@ use abi::{FnType, FnTypeExt}; use common::*; +use llvm; use rustc::hir; use rustc::ty::{self, Ty, TypeFoldable}; use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; @@ -428,8 +429,13 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { PointerKind::Shared }, hir::MutMutable => { - if cx.tcx.sess.opts.debugging_opts.mutable_noalias || - cx.tcx.sess.panic_strategy() == PanicStrategy::Abort { + // Only emit noalias annotations for LLVM >= 6 or in panic=abort + // mode, as prior versions had many bugs in conjunction with + // unwinding. See also issue #31681. + let mutable_noalias = cx.tcx.sess.opts.debugging_opts.mutable_noalias + .unwrap_or(unsafe { llvm::LLVMRustVersionMajor() >= 6 } + || cx.tcx.sess.panic_strategy() == PanicStrategy::Abort); + if mutable_noalias { PointerKind::UniqueBorrowed } else { PointerKind::Shared diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs index 1a62f39ae3d..2063db6dc53 100644 --- a/src/librustc_codegen_utils/symbol_names.rs +++ b/src/librustc_codegen_utils/symbol_names.rs @@ -97,18 +97,19 @@ //! virtually impossible. Thus, symbol hash generation exclusively relies on //! DefPaths which are much more robust in the face of changes to the code base. -use rustc::middle::weak_lang_items; -use rustc_mir::monomorphize::Instance; -use rustc_mir::monomorphize::item::{MonoItem, MonoItemExt, InstantiationMode}; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::hir::map as hir_map; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::fold::TypeVisitor; +use rustc::hir::map::definitions::DefPathData; +use rustc::ich::NodeIdHashingMode; +use rustc::middle::weak_lang_items; use rustc::ty::item_path::{self, ItemPathBuffer, RootMode}; use rustc::ty::maps::Providers; use rustc::ty::subst::Substs; -use rustc::hir::map::definitions::DefPathData; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::util::common::record_time; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_mir::monomorphize::item::{InstantiationMode, MonoItem, MonoItemExt}; +use rustc_mir::monomorphize::Instance; use syntax::attr; use syntax_pos::symbol::Symbol; @@ -124,51 +125,60 @@ pub fn provide(providers: &mut Providers) { }; } -fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, +fn get_symbol_hash<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, - // the DefId of the item this name is for - def_id: DefId, + // the DefId of the item this name is for + def_id: DefId, - // instance this name will be for - instance: Instance<'tcx>, + // instance this name will be for + instance: Instance<'tcx>, - // type of the item, without any generic - // parameters substituted; this is - // included in the hash as a kind of - // safeguard. - item_type: Ty<'tcx>, + // type of the item, without any generic + // parameters substituted; this is + // included in the hash as a kind of + // safeguard. + item_type: Ty<'tcx>, - // values for generic type parameters, - // if any. - substs: &'tcx Substs<'tcx>) - -> u64 { - debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, substs); + // values for generic type parameters, + // if any. + substs: &'tcx Substs<'tcx>, +) -> u64 { + debug!( + "get_symbol_hash(def_id={:?}, parameters={:?})", + def_id, substs + ); - let mut hasher = ty::util::TypeIdHasher::<u64>::new(tcx); + let mut hasher = StableHasher::<u64>::new(); + let mut hcx = tcx.create_stable_hashing_context(); record_time(&tcx.sess.perf_stats.symbol_hash_time, || { // the main symbol name is not necessarily unique; hash in the // compiler's internal def-path, guaranteeing each symbol has a // truly unique path - hasher.hash(tcx.def_path_hash(def_id)); + tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher); // Include the main item-type. Note that, in this case, the // assertions about `needs_subst` may not hold, but this item-type // ought to be the same for every reference anyway. assert!(!item_type.has_erasable_regions()); - hasher.visit_ty(item_type); + hcx.while_hashing_spans(false, |hcx| { + hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + item_type.hash_stable(hcx, &mut hasher); + }); + }); // If this is a function, we hash the signature as well. // This is not *strictly* needed, but it may help in some // situations, see the `run-make/a-b-a-linker-guard` test. if let ty::TyFnDef(..) = item_type.sty { - item_type.fn_sig(tcx).visit_with(&mut hasher); + item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher); } // also include any type parameters (for generic items) assert!(!substs.has_erasable_regions()); assert!(!substs.needs_subst()); - substs.visit_with(&mut hasher); + substs.hash_stable(&mut hcx, &mut hasher); let is_generic = substs.types().next().is_some(); let avoid_cross_crate_conflicts = @@ -194,12 +204,11 @@ fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if !def_id.is_local() && tcx.share_generics() { // If we are re-using a monomorphization from another crate, // we have to compute the symbol hash accordingly. - let upstream_monomorphizations = - tcx.upstream_monomorphizations_for(def_id); + let upstream_monomorphizations = tcx.upstream_monomorphizations_for(def_id); - upstream_monomorphizations.and_then(|monos| monos.get(&substs) - .cloned()) - .unwrap_or(LOCAL_CRATE) + upstream_monomorphizations + .and_then(|monos| monos.get(&substs).cloned()) + .unwrap_or(LOCAL_CRATE) } else { LOCAL_CRATE } @@ -207,8 +216,9 @@ fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, LOCAL_CRATE }; - hasher.hash(&tcx.original_crate_name(instantiating_crate).as_str()[..]); - hasher.hash(&tcx.crate_disambiguator(instantiating_crate)); + (&tcx.original_crate_name(instantiating_crate).as_str()[..]) + .hash_stable(&mut hcx, &mut hasher); + (&tcx.crate_disambiguator(instantiating_crate)).hash_stable(&mut hcx, &mut hasher); } }); @@ -216,9 +226,7 @@ fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hasher.finish() } -fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) - -> ty::SymbolName -{ +fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::SymbolName { let mut buffer = SymbolPathBuffer::new(); item_path::with_forced_absolute_paths(|| { tcx.push_item_path(&mut buffer, def_id); @@ -226,20 +234,17 @@ fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) buffer.into_interned() } -fn symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) - -> ty::SymbolName -{ - ty::SymbolName { name: Symbol::intern(&compute_symbol_name(tcx, instance)).as_interned_str() } +fn symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) -> ty::SymbolName { + ty::SymbolName { + name: Symbol::intern(&compute_symbol_name(tcx, instance)).as_interned_str(), + } } -fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) - -> String -{ +fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) -> String { let def_id = instance.def_id(); let substs = instance.substs; - debug!("symbol_name(def_id={:?}, substs={:?})", - def_id, substs); + debug!("symbol_name(def_id={:?}, substs={:?})", def_id, substs); let node_id = tcx.hir.as_local_node_id(def_id); @@ -259,7 +264,7 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance let is_foreign = if let Some(id) = node_id { match tcx.hir.get(id) { hir_map::NodeForeignItem(_) => true, - _ => false + _ => false, } } else { tcx.is_foreign_item(def_id) @@ -296,8 +301,7 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance loop { let key = tcx.def_key(ty_def_id); match key.disambiguated_data.data { - DefPathData::TypeNs(_) | - DefPathData::ValueNs(_) => { + DefPathData::TypeNs(_) | DefPathData::ValueNs(_) => { instance_ty = tcx.type_of(ty_def_id); break; } @@ -306,8 +310,12 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance // to be a value or type-def or something in there // *somewhere* ty_def_id.index = key.parent.unwrap_or_else(|| { - bug!("finding type for {:?}, encountered def-id {:?} with no \ - parent", def_id, ty_def_id); + bug!( + "finding type for {:?}, encountered def-id {:?} with no \ + parent", + def_id, + ty_def_id + ); }); } } @@ -337,14 +345,14 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance // use C++ name-mangling. struct SymbolPathBuffer { result: String, - temp_buf: String + temp_buf: String, } impl SymbolPathBuffer { fn new() -> Self { let mut result = SymbolPathBuffer { result: String::with_capacity(64), - temp_buf: String::with_capacity(16) + temp_buf: String::with_capacity(16), }; result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested result @@ -353,14 +361,16 @@ impl SymbolPathBuffer { fn from_interned(symbol: ty::SymbolName) -> Self { let mut result = SymbolPathBuffer { result: String::with_capacity(64), - temp_buf: String::with_capacity(16) + temp_buf: String::with_capacity(16), }; result.result.push_str(&symbol.name.as_str()); result } fn into_interned(self) -> ty::SymbolName { - ty::SymbolName { name: Symbol::intern(&self.result).as_interned_str() } + ty::SymbolName { + name: Symbol::intern(&self.result).as_interned_str(), + } } fn finish(mut self, hash: u64) -> String { @@ -379,7 +389,11 @@ impl ItemPathBuffer for SymbolPathBuffer { fn push(&mut self, text: &str) { self.temp_buf.clear(); let need_underscore = sanitize(&mut self.temp_buf, text); - let _ = write!(self.result, "{}", self.temp_buf.len() + (need_underscore as usize)); + let _ = write!( + self.result, + "{}", + self.temp_buf.len() + (need_underscore as usize) + ); if need_underscore { self.result.push('_'); } @@ -410,16 +424,13 @@ pub fn sanitize(result: &mut String, s: &str) -> bool { '-' | ':' => result.push('.'), // These are legal symbols - 'a' ... 'z' - | 'A' ... 'Z' - | '0' ... '9' - | '_' | '.' | '$' => result.push(c), + 'a'...'z' | 'A'...'Z' | '0'...'9' | '_' | '.' | '$' => result.push(c), _ => { result.push('$'); for c in c.escape_unicode().skip(1) { match c { - '{' => {}, + '{' => {} '}' => result.push('$'), c => result.push(c), } @@ -429,7 +440,6 @@ pub fn sanitize(result: &mut String, s: &str) -> bool { } // Underscore-qualify anything that didn't start as an ident. - !result.is_empty() && - result.as_bytes()[0] != '_' as u8 && - ! (result.as_bytes()[0] as char).is_xid_start() + !result.is_empty() && result.as_bytes()[0] != '_' as u8 + && !(result.as_bytes()[0] as char).is_xid_start() } diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index 6f1cbcad2f4..bb1fb84a0ce 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -9,7 +9,7 @@ path = "lib.rs" crate-type = ["dylib"] [dependencies] -ena = "0.9.1" +ena = "0.9.3" log = "0.4" rustc_cratesio_shim = { path = "../librustc_cratesio_shim" } serialize = { path = "../libserialize" } diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index 28e3180063c..a22dd1fecec 100644 --- a/src/librustc_data_structures/bitvec.rs +++ b/src/librustc_data_structures/bitvec.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::collections::BTreeMap; +use indexed_vec::{Idx, IndexVec}; use std::collections::btree_map::Entry; -use std::marker::PhantomData; +use std::collections::BTreeMap; use std::iter::FromIterator; -use indexed_vec::{Idx, IndexVec}; +use std::marker::PhantomData; type Word = u128; const WORD_BITS: usize = 128; @@ -317,14 +317,25 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { if read != write { let (bit_set_read, bit_set_write) = self.vector.pick2_mut(read, write); - for read_val in bit_set_read.iter() { - changed = changed | bit_set_write.insert(read_val); + for read_chunk in bit_set_read.chunks() { + changed = changed | bit_set_write.insert_chunk(read_chunk).any(); } } changed } + /// True if `sub` is a subset of `sup` + pub fn is_subset(&self, sub: R, sup: R) -> bool { + sub == sup || { + let bit_set_sub = &self.vector[sub]; + let bit_set_sup = &self.vector[sup]; + bit_set_sub + .chunks() + .all(|read_chunk| read_chunk.bits_eq(bit_set_sup.contains_chunk(read_chunk))) + } + } + /// Iterates through all the columns set to true in a given row of /// the matrix. pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a { @@ -346,6 +357,7 @@ pub struct SparseChunk<I> { } impl<I: Idx> SparseChunk<I> { + #[inline] pub fn one(index: I) -> Self { let index = index.index(); let key_usize = index / 128; @@ -358,10 +370,16 @@ impl<I: Idx> SparseChunk<I> { } } + #[inline] pub fn any(&self) -> bool { self.bits != 0 } + #[inline] + pub fn bits_eq(&self, other: SparseChunk<I>) -> bool { + self.bits == other.bits + } + pub fn iter(&self) -> impl Iterator<Item = I> { let base = self.key as usize * 128; let mut bits = self.bits; @@ -394,6 +412,10 @@ impl<I: Idx> SparseBitSet<I> { self.chunk_bits.len() * 128 } + /// Returns a chunk containing only those bits that are already + /// present. You can test therefore if `self` contains all the + /// bits in chunk already by doing `chunk == + /// self.contains_chunk(chunk)`. pub fn contains_chunk(&self, chunk: SparseChunk<I>) -> SparseChunk<I> { SparseChunk { bits: self.chunk_bits @@ -403,6 +425,11 @@ impl<I: Idx> SparseBitSet<I> { } } + /// Modifies `self` to contain all the bits from `chunk` (in + /// addition to any pre-existing bits); returns a new chunk that + /// contains only those bits that were newly added. You can test + /// if anything was inserted by invoking `any()` on the returned + /// value. pub fn insert_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> { if chunk.bits == 0 { return chunk; diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index cede6f14782..6ee8c3579f5 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -67,6 +67,12 @@ impl<K, V> SnapshotMap<K, V> } } + pub fn insert_noop(&mut self) { + if !self.undo_log.is_empty() { + self.undo_log.push(UndoLog::Noop); + } + } + pub fn remove(&mut self, key: K) -> bool { match self.map.remove(&key) { Some(old_value) => { diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 0b4b090f1f0..2f89814032e 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -108,7 +108,7 @@ use syntax::ast; use syntax::codemap::{CodeMap, FileLoader, RealFileLoader}; use syntax::feature_gate::{GatedCfg, UnstableFeatures}; use syntax::parse::{self, PResult}; -use syntax_pos::{DUMMY_SP, MultiSpan, FileName}; +use syntax_pos::{hygiene, DUMMY_SP, MultiSpan, FileName}; #[cfg(test)] mod test; @@ -466,6 +466,7 @@ pub fn run_compiler<'a>(args: &[String], }; let (sopts, cfg) = config::build_session_options_and_crate_config(&matches); + hygiene::set_default_edition(sopts.edition); driver::spawn_thread_pool(sopts, |sopts| { run_compiler_with_pool(matches, sopts, cfg, callbacks, file_loader, emitter_dest) diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 0ae133640fa..cfd5cf5a0f9 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -177,6 +177,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UNUSED_DOC_COMMENT, UNUSED_EXTERN_CRATES, UNUSED_FEATURES, + UNUSED_LABELS, UNUSED_PARENS); add_lint_group!(sess, @@ -226,11 +227,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { edition: None, }, FutureIncompatibleInfo { - id: LintId::of(LEGACY_IMPORTS), - reference: "issue #38260 <https://github.com/rust-lang/rust/issues/38260>", - edition: None, - }, - FutureIncompatibleInfo { id: LintId::of(LEGACY_CONSTRUCTOR_VISIBILITY), reference: "issue #39207 <https://github.com/rust-lang/rust/issues/39207>", edition: None, @@ -318,6 +314,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { "converted into hard error, see https://github.com/rust-lang/rust/issues/36892"); store.register_removed("extra_requirement_in_impl", "converted into hard error, see https://github.com/rust-lang/rust/issues/37166"); + store.register_removed("legacy_imports", + "converted into hard error, see https://github.com/rust-lang/rust/issues/38260"); store.register_removed("coerce_never", "converted into hard error, see https://github.com/rust-lang/rust/issues/48950"); store.register_removed("resolve_trait_on_defaulted_unit", diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 87b3a2dc69f..6c1f72f5f9c 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -35,6 +35,7 @@ use std::{cmp, fs}; use syntax::ast; use syntax::attr; +use syntax::edition::Edition; use syntax::ext::base::SyntaxExtension; use syntax::symbol::Symbol; use syntax::visit; @@ -535,7 +536,10 @@ impl<'a> CrateLoader<'a> { mem::transmute::<*mut u8, fn(&mut Registry)>(sym) }; - struct MyRegistrar(Vec<(ast::Name, Lrc<SyntaxExtension>)>); + struct MyRegistrar { + extensions: Vec<(ast::Name, Lrc<SyntaxExtension>)>, + edition: Edition, + } impl Registry for MyRegistrar { fn register_custom_derive(&mut self, @@ -544,36 +548,38 @@ impl<'a> CrateLoader<'a> { attributes: &[&'static str]) { let attrs = attributes.iter().cloned().map(Symbol::intern).collect::<Vec<_>>(); let derive = ProcMacroDerive::new(expand, attrs.clone()); - let derive = SyntaxExtension::ProcMacroDerive(Box::new(derive), attrs); - self.0.push((Symbol::intern(trait_name), Lrc::new(derive))); + let derive = SyntaxExtension::ProcMacroDerive( + Box::new(derive), attrs, self.edition + ); + self.extensions.push((Symbol::intern(trait_name), Lrc::new(derive))); } fn register_attr_proc_macro(&mut self, name: &str, expand: fn(TokenStream, TokenStream) -> TokenStream) { let expand = SyntaxExtension::AttrProcMacro( - Box::new(AttrProcMacro { inner: expand }) + Box::new(AttrProcMacro { inner: expand }), self.edition ); - self.0.push((Symbol::intern(name), Lrc::new(expand))); + self.extensions.push((Symbol::intern(name), Lrc::new(expand))); } fn register_bang_proc_macro(&mut self, name: &str, expand: fn(TokenStream) -> TokenStream) { let expand = SyntaxExtension::ProcMacro( - Box::new(BangProcMacro { inner: expand }) + Box::new(BangProcMacro { inner: expand }), self.edition ); - self.0.push((Symbol::intern(name), Lrc::new(expand))); + self.extensions.push((Symbol::intern(name), Lrc::new(expand))); } } - let mut my_registrar = MyRegistrar(Vec::new()); + let mut my_registrar = MyRegistrar { extensions: Vec::new(), edition: root.edition }; registrar(&mut my_registrar); // Intentionally leak the dynamic library. We can't ever unload it // since the library can make things that will live arbitrarily long. mem::forget(lib); - my_registrar.0 + my_registrar.extensions } /// Look for a plugin registrar. Returns library path, crate diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 9bbce563b61..f2d2d090e0a 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -24,6 +24,7 @@ use rustc::util::nodemap::{FxHashMap, NodeMap}; use rustc_data_structures::sync::{Lrc, RwLock, Lock}; use syntax::{ast, attr}; +use syntax::edition::Edition; use syntax::ext::base::SyntaxExtension; use syntax::symbol::Symbol; use syntax_pos; @@ -234,4 +235,8 @@ impl CrateMetadata { pub fn panic_strategy(&self) -> PanicStrategy { self.root.panic_strategy.clone() } + + pub fn edition(&self) -> Edition { + self.root.edition + } } diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index fdfe2a98bf9..c8f25f935e9 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -38,6 +38,7 @@ use std::sync::Arc; use syntax::ast; use syntax::attr; use syntax::codemap; +use syntax::edition::Edition; use syntax::ext::base::SyntaxExtension; use syntax::parse::filemap_to_stream; use syntax::symbol::Symbol; @@ -464,6 +465,11 @@ impl CrateStore for cstore::CStore { self.get_crate_data(cnum).hash() } + fn crate_edition_untracked(&self, cnum: CrateNum) -> Edition + { + self.get_crate_data(cnum).edition() + } + /// 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. @@ -512,7 +518,8 @@ impl CrateStore for cstore::CStore { return LoadedMacro::ProcMacro(proc_macros[id.index.to_proc_macro_index()].1.clone()); } else if data.name == "proc_macro" && self.get_crate_data(id.krate).item_name(id.index) == "quote" { - let ext = SyntaxExtension::ProcMacro(Box::new(::proc_macro::__internal::Quoter)); + let ext = SyntaxExtension::ProcMacro(Box::new(::proc_macro::__internal::Quoter), + data.edition()); return LoadedMacro::ProcMacro(Lrc::new(ext)); } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index bbc4120f060..d00f4f32c10 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -44,7 +44,7 @@ use syntax::ast::{self, CRATE_NODE_ID}; use syntax::codemap::Spanned; use syntax::attr; use syntax::symbol::Symbol; -use syntax_pos::{self, FileName, FileMap, Span, DUMMY_SP}; +use syntax_pos::{self, hygiene, FileName, FileMap, Span, DUMMY_SP}; use rustc::hir::{self, PatKind}; use rustc::hir::itemlikevisit::ItemLikeVisitor; @@ -496,6 +496,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hash: link_meta.crate_hash, disambiguator: tcx.sess.local_crate_disambiguator(), panic_strategy: tcx.sess.panic_strategy(), + edition: hygiene::default_edition(), has_global_allocator: has_global_allocator, has_default_lib_allocator: has_default_lib_allocator, plugin_registrar_fn: tcx.sess diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index c9949389ace..8e17b7f8d69 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -23,6 +23,7 @@ use rustc_target::spec::{PanicStrategy, TargetTriple}; use rustc_serialize as serialize; use syntax::{ast, attr}; +use syntax::edition::Edition; use syntax::symbol::Symbol; use syntax_pos::{self, Span}; @@ -189,6 +190,7 @@ pub struct CrateRoot { pub hash: hir::svh::Svh, pub disambiguator: CrateDisambiguator, pub panic_strategy: PanicStrategy, + pub edition: Edition, pub has_global_allocator: bool, pub has_default_lib_allocator: bool, pub plugin_registrar_fn: Option<DefIndex>, diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 1cc69351b47..5d6d4619c5e 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -10,7 +10,7 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. -use borrow_check::nll::region_infer::{RegionCausalInfo, RegionInferenceContext}; +use borrow_check::nll::region_infer::RegionInferenceContext; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; @@ -248,7 +248,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( nonlexical_regioncx: regioncx, used_mut: FxHashSet(), used_mut_upvars: SmallVec::new(), - nonlexical_cause_info: None, borrow_set, dominators, }; @@ -367,7 +366,6 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { /// contains the results from region inference and lets us e.g. /// find out which CFG points are contained in each borrow region. nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>, - nonlexical_cause_info: Option<RegionCausalInfo>, /// The set of borrows extracted from the MIR borrow_set: Rc<BorrowSet<'tcx>>, @@ -1810,9 +1808,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } Reservation(WriteKind::Move) + | Write(WriteKind::Move) | Reservation(WriteKind::StorageDeadOrDrop) | Reservation(WriteKind::MutableBorrow(BorrowKind::Shared)) - | Write(WriteKind::Move) | Write(WriteKind::StorageDeadOrDrop) | Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => { if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) { @@ -1851,8 +1849,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // mutated, then it is justified to be annotated with the `mut` // keyword, since the mutation may be a possible reassignment. let mpi = self.move_data.rev_lookup.find_local(*local); - if flow_state.inits.contains(&mpi) { - self.used_mut.insert(*local); + let ii = &self.move_data.init_path_map[mpi]; + for index in ii { + if flow_state.ever_inits.contains(index) { + self.used_mut.insert(*local); + } } } } diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs index 56e388a5b60..2807a4e8857 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -32,13 +32,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let regioncx = &&self.nonlexical_regioncx; let mir = self.mir; - if self.nonlexical_cause_info.is_none() { - self.nonlexical_cause_info = Some(regioncx.compute_causal_info(mir)); - } - - let cause_info = self.nonlexical_cause_info.as_ref().unwrap(); - if let Some(cause) = cause_info.why_region_contains_point(borrow.region, context.loc) { - match *cause.root_cause() { + let borrow_region_vid = regioncx.to_region_vid(borrow.region); + if let Some(cause) = regioncx.why_region_contains_point(borrow_region_vid, context.loc) { + match cause { Cause::LiveVar(local, location) => { match find_regular_use(mir, regioncx, borrow, location, local) { Some(p) => { diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 0b1729294d8..a162ef36a60 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -240,11 +240,11 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( }); // Also dump the inference graph constraints as a graphviz file. - let _: io::Result<()> = do_catch! {{ + let _: io::Result<()> = do catch { let mut file = pretty::create_dump_file(infcx.tcx, "regioncx.dot", None, "nll", &0, source)?; regioncx.dump_graphviz(&mut file)?; - }}; + }; } fn dump_annotation<'a, 'gcx, 'tcx>( diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs deleted file mode 100644 index f68394d6149..00000000000 --- a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <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. - -//! Module defining the `dfs` method on `RegionInferenceContext`, along with -//! its associated helper traits. - -use borrow_check::nll::universal_regions::UniversalRegions; -use borrow_check::nll::region_infer::RegionInferenceContext; -use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements, - RegionValues}; -use syntax::codemap::Span; -use rustc::mir::{Location, Mir}; -use rustc::ty::RegionVid; -use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::indexed_vec::Idx; - -pub(super) struct DfsStorage { - stack: Vec<Location>, - visited: BitVector, -} - -impl<'tcx> RegionInferenceContext<'tcx> { - /// Creates dfs storage for use by dfs; this should be shared - /// across as many calls to dfs as possible to amortize allocation - /// costs. - pub(super) fn new_dfs_storage(&self) -> DfsStorage { - let num_elements = self.elements.num_elements(); - DfsStorage { - stack: vec![], - visited: BitVector::new(num_elements), - } - } - - /// Function used to satisfy or test a `R1: R2 @ P` - /// constraint. The core idea is that it performs a DFS starting - /// from `P`. The precise actions *during* that DFS depend on the - /// `op` supplied, so see (e.g.) `CopyFromSourceToTarget` for more - /// details. - /// - /// Returns: - /// - /// - `Ok(true)` if the walk was completed and something changed - /// along the way; - /// - `Ok(false)` if the walk was completed with no changes; - /// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the - /// value that `op` returned. - #[inline(never)] // ensure dfs is identifiable in profiles - pub(super) fn dfs<C>( - &self, - mir: &Mir<'tcx>, - dfs: &mut DfsStorage, - mut op: C, - ) -> Result<bool, C::Early> - where - C: DfsOp, - { - let mut changed = false; - - dfs.visited.clear(); - dfs.stack.push(op.start_point()); - while let Some(p) = dfs.stack.pop() { - let point_index = self.elements.index(p); - - if !op.source_region_contains(point_index) { - debug!(" not in from-region"); - continue; - } - - if !dfs.visited.insert(point_index.index()) { - debug!(" already visited"); - continue; - } - - let new = op.add_to_target_region(point_index)?; - changed |= new; - - let block_data = &mir[p.block]; - - let start_stack_len = dfs.stack.len(); - - if p.statement_index < block_data.statements.len() { - dfs.stack.push(Location { - statement_index: p.statement_index + 1, - ..p - }); - } else { - dfs.stack.extend( - block_data - .terminator() - .successors() - .map(|&basic_block| Location { - statement_index: 0, - block: basic_block, - }), - ); - } - - if dfs.stack.len() == start_stack_len { - // If we reach the END point in the graph, then copy - // over any skolemized end points in the `from_region` - // and make sure they are included in the `to_region`. - changed |= op.add_universal_regions_outlived_by_source_to_target()?; - } - } - - Ok(changed) - } -} - -/// Customizes the operation of the `dfs` function. This function is -/// used during inference to satisfy a `R1: R2 @ P` constraint. -pub(super) trait DfsOp { - /// If this op stops the walk early, what type does it propagate? - type Early; - - /// Returns the point from which to start the DFS. - fn start_point(&self) -> Location; - - /// Returns true if the source region contains the given point. - fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool; - - /// Adds the given point to the target region, returning true if - /// something has changed. Returns `Err` if we should abort the - /// walk early. - fn add_to_target_region( - &mut self, - point_index: RegionElementIndex, - ) -> Result<bool, Self::Early>; - - /// Adds all universal regions in the source region to the target region, returning - /// true if something has changed. - fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result<bool, Self::Early>; -} - -/// Used during inference to enforce a `R1: R2 @ P` constraint. For -/// each point Q we reach along the DFS, we check if Q is in R2 (the -/// "source region"). If not, we stop the walk. Otherwise, we add Q to -/// R1 (the "target region") and continue to Q's successors. If we -/// reach the end of the graph, then we add any universal regions from -/// R2 into R1. -pub(super) struct CopyFromSourceToTarget<'v> { - pub source_region: RegionVid, - pub target_region: RegionVid, - pub inferred_values: &'v mut RegionValues, - pub constraint_point: Location, - pub constraint_span: Span, -} - -impl<'v> DfsOp for CopyFromSourceToTarget<'v> { - /// We never stop the walk early. - type Early = !; - - fn start_point(&self) -> Location { - self.constraint_point - } - - fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { - self.inferred_values - .contains(self.source_region, point_index) - } - - fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result<bool, !> { - Ok(self.inferred_values.add_due_to_outlives( - self.source_region, - self.target_region, - point_index, - self.constraint_point, - self.constraint_span, - )) - } - - fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result<bool, !> { - Ok(self.inferred_values.add_universal_regions_outlived_by( - self.source_region, - self.target_region, - self.constraint_point, - self.constraint_span, - )) - } -} - -/// Used after inference to *test* a `R1: R2 @ P` constraint. For -/// each point Q we reach along the DFS, we check if Q in R2 is also -/// contained in R1. If not, we abort the walk early with an `Err` -/// condition. Similarly, if we reach the end of the graph and find -/// that R1 contains some universal region that R2 does not contain, -/// we abort the walk early. -pub(super) struct TestTargetOutlivesSource<'v, 'tcx: 'v> { - pub source_region: RegionVid, - pub target_region: RegionVid, - pub elements: &'v RegionValueElements, - pub universal_regions: &'v UniversalRegions<'tcx>, - pub inferred_values: &'v RegionValues, - pub constraint_point: Location, -} - -impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> { - /// The element that was not found within R2. - type Early = RegionElementIndex; - - fn start_point(&self) -> Location { - self.constraint_point - } - - fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { - self.inferred_values - .contains(self.source_region, point_index) - } - - fn add_to_target_region( - &mut self, - point_index: RegionElementIndex, - ) -> Result<bool, RegionElementIndex> { - if !self.inferred_values - .contains(self.target_region, point_index) - { - return Err(point_index); - } - - Ok(false) - } - - fn add_universal_regions_outlived_by_source_to_target( - &mut self, - ) -> Result<bool, RegionElementIndex> { - // For all `ur_in_source` in `source_region`. - for ur_in_source in self.inferred_values - .universal_regions_outlived_by(self.source_region) - { - // Check that `target_region` outlives `ur_in_source`. - - // If `ur_in_source` is a member of `target_region`, OK. - // - // (This is implied by the loop below, actually, just an - // irresistible micro-opt. Mm. Premature optimization. So - // tasty.) - if self.inferred_values - .contains(self.target_region, ur_in_source) - { - continue; - } - - // If there is some other element X such that `target_region: X` and - // `X: ur_in_source`, OK. - if self.inferred_values - .universal_regions_outlived_by(self.target_region) - .any(|ur_in_target| self.universal_regions.outlives(ur_in_target, ur_in_source)) - { - continue; - } - - // Otherwise, not known to be true. - return Err(self.elements.index(ur_in_source)); - } - - Ok(false) - } -} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 57b8824191f..5a1ab73b2b8 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -9,16 +9,19 @@ // except according to those terms. use super::universal_regions::UniversalRegions; +use borrow_check::nll::region_infer::values::ToElementIndex; use rustc::hir::def_id::DefId; +use rustc::infer::error_reporting::nice_region_error::NiceRegionError; +use rustc::infer::region_constraints::{GenericKind, VarInfos}; use rustc::infer::InferCtxt; use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::RegionObligation; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; -use rustc::infer::error_reporting::nice_region_error::NiceRegionError; -use rustc::infer::region_constraints::{GenericKind, VarInfos}; -use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - Local, Location, Mir}; +use rustc::mir::{ + ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location, + Mir, +}; use rustc::traits::ObligationCause; use rustc::ty::{self, RegionVid, Ty, TypeFoldable}; use rustc::util::common::{self, ErrorReported}; @@ -30,8 +33,6 @@ use syntax::ast; use syntax_pos::Span; mod annotation; -mod dfs; -use self::dfs::{CopyFromSourceToTarget, TestTargetOutlivesSource}; mod dump_mir; mod graphviz; mod values; @@ -100,7 +101,7 @@ struct RegionDefinition<'tcx> { /// NB: The variants in `Cause` are intentionally ordered. Lower /// values are preferred when it comes to error messages. Do not /// reorder willy nilly. -#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] pub(crate) enum Cause { /// point inserted because Local was live at the given Location LiveVar(Local, Location), @@ -114,23 +115,6 @@ pub(crate) enum Cause { /// part of the initial set of values for a universally quantified region UniversalRegion(RegionVid), - - /// Element E was added to R because there was some - /// outlives obligation `R: R1 @ P` and `R1` contained `E`. - Outlives { - /// the reason that R1 had E - original_cause: Rc<Cause>, - - /// the point P from the relation - constraint_location: Location, - - /// The span indicating why we added the outlives constraint. - constraint_span: Span, - }, -} - -pub(crate) struct RegionCausalInfo { - inferred_values: RegionValues, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -325,7 +309,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Add all nodes in the CFG to liveness constraints for point_index in self.elements.all_point_indices() { - self.liveness_constraints.add( + self.liveness_constraints.add_element( variable, point_index, &Cause::UniversalRegion(variable), @@ -333,8 +317,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // Add `end(X)` into the set for X. - self.liveness_constraints - .add(variable, variable, &Cause::UniversalRegion(variable)); + self.liveness_constraints.add_element( + variable, + variable, + &Cause::UniversalRegion(variable), + ); } } @@ -383,7 +370,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("add_live_point: @{:?} Adding cause {:?}", point, cause); let element = self.elements.index(point); - if self.liveness_constraints.add(v, element, &cause) { + if self.liveness_constraints.add_element(v, element, &cause) { true } else { false @@ -438,9 +425,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> Option<ClosureRegionRequirements<'gcx>> { assert!(self.inferred_values.is_none(), "values already inferred"); - let dfs_storage = &mut self.new_dfs_storage(); - - self.propagate_constraints(mir, dfs_storage); + self.propagate_constraints(mir); // If this is a closure, we can propagate unsatisfied // `outlives_requirements` to our creator, so create a vector @@ -453,13 +438,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { None }; - self.check_type_tests( - infcx, - mir, - dfs_storage, - mir_def_id, - outlives_requirements.as_mut(), - ); + self.check_type_tests(infcx, mir, mir_def_id, outlives_requirements.as_mut()); self.check_universal_regions(infcx, mir_def_id, outlives_requirements.as_mut()); @@ -476,31 +455,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - /// Re-execute the region inference, this time tracking causal information. - /// This is significantly slower, so it is done only when an error is being reported. - pub(super) fn compute_causal_info(&self, mir: &Mir<'tcx>) -> RegionCausalInfo { - let dfs_storage = &mut self.new_dfs_storage(); - let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(true)); - RegionCausalInfo { inferred_values } - } - /// Propagate the region constraints: this will grow the values /// for each region variable until all the constraints are /// satisfied. Note that some values may grow **too** large to be /// feasible, but we check this later. - fn propagate_constraints(&mut self, mir: &Mir<'tcx>, dfs_storage: &mut dfs::DfsStorage) { + fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { self.dependency_map = Some(self.build_dependency_map()); - let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(false)); + let inferred_values = self.compute_region_values(mir); self.inferred_values = Some(inferred_values); } #[inline(never)] // ensure dfs is identifiable in profiles - fn compute_region_values( - &self, - mir: &Mir<'tcx>, - dfs_storage: &mut dfs::DfsStorage, - track_causes: TrackCauses, - ) -> RegionValues { + fn compute_region_values(&self, _mir: &Mir<'tcx>) -> RegionValues { debug!("compute_region_values()"); debug!("compute_region_values: constraints={:#?}", { let mut constraints: Vec<_> = self.constraints.iter().collect(); @@ -510,7 +476,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // The initial values for each region are derived from the liveness // constraints we have accumulated. - let mut inferred_values = self.liveness_constraints.duplicate(track_causes); + let mut inferred_values = self.liveness_constraints.duplicate(TrackCauses(false)); let dependency_map = self.dependency_map.as_ref().unwrap(); @@ -527,21 +493,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let constraint = &self.constraints[constraint_idx]; debug!("propagate_constraints: constraint={:?}", constraint); - // Grow the value as needed to accommodate the - // outlives constraint. - let Ok(made_changes) = self.dfs( - mir, - dfs_storage, - CopyFromSourceToTarget { - source_region: constraint.sub, - target_region: constraint.sup, - inferred_values: &mut inferred_values, - constraint_point: constraint.point, - constraint_span: constraint.span, - }, - ); - - if made_changes { + if inferred_values.add_region(constraint.sup, constraint.sub) { debug!("propagate_constraints: sub={:?}", constraint.sub); debug!("propagate_constraints: sup={:?}", constraint.sup); @@ -586,7 +538,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, - dfs_storage: &mut dfs::DfsStorage, mir_def_id: DefId, mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>, ) { @@ -595,13 +546,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); - if self.eval_region_test( - mir, - dfs_storage, - type_test.point, - type_test.lower_bound, - &type_test.test, - ) { + if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) { continue; } @@ -858,7 +803,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn eval_region_test( &self, mir: &Mir<'tcx>, - dfs_storage: &mut dfs::DfsStorage, point: Location, lower_bound: RegionVid, test: &RegionTest, @@ -871,27 +815,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { match test { RegionTest::IsOutlivedByAllRegionsIn(regions) => regions .iter() - .all(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)), + .all(|&r| self.eval_outlives(mir, r, lower_bound, point)), RegionTest::IsOutlivedByAnyRegionIn(regions) => regions .iter() - .any(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)), + .any(|&r| self.eval_outlives(mir, r, lower_bound, point)), RegionTest::Any(tests) => tests .iter() - .any(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)), + .any(|test| self.eval_region_test(mir, point, lower_bound, test)), RegionTest::All(tests) => tests .iter() - .all(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)), + .all(|test| self.eval_region_test(mir, point, lower_bound, test)), } } // Evaluate whether `sup_region: sub_region @ point`. fn eval_outlives( &self, - mir: &Mir<'tcx>, - dfs_storage: &mut dfs::DfsStorage, + _mir: &Mir<'tcx>, sup_region: RegionVid, sub_region: RegionVid, point: Location, @@ -901,36 +844,46 @@ impl<'tcx> RegionInferenceContext<'tcx> { sup_region, sub_region, point ); - // Roughly speaking, do a DFS of all region elements reachable - // from `point` contained in `sub_region`. If any of those are - // *not* present in `sup_region`, the DFS will abort early and - // yield an `Err` result. - match self.dfs( - mir, - dfs_storage, - TestTargetOutlivesSource { - source_region: sub_region, - target_region: sup_region, - constraint_point: point, - elements: &self.elements, - universal_regions: &self.universal_regions, - inferred_values: self.inferred_values.as_ref().unwrap(), - }, - ) { - Ok(_) => { - debug!("eval_outlives: true"); - true - } + let inferred_values = self.inferred_values + .as_ref() + .expect("values for regions not yet inferred"); - Err(elem) => { - debug!( - "eval_outlives: false because `{:?}` is not present in `{:?}`", - self.elements.to_element(elem), - sup_region - ); - false - } + debug!( + "eval_outlives: sup_region's value = {:?}", + inferred_values.region_value_str(sup_region), + ); + debug!( + "eval_outlives: sub_region's value = {:?}", + inferred_values.region_value_str(sub_region), + ); + + // Both the `sub_region` and `sup_region` consist of the union + // of some number of universal regions (along with the union + // of various points in the CFG; ignore those points for + // now). Therefore, the sup-region outlives the sub-region if, + // for each universal region R1 in the sub-region, there + // exists some region R2 in the sup-region that outlives R1. + let universal_outlives = inferred_values + .universal_regions_outlived_by(sub_region) + .all(|r1| { + inferred_values + .universal_regions_outlived_by(sup_region) + .any(|r2| self.universal_regions.outlives(r2, r1)) + }); + + if !universal_outlives { + return false; + } + + // Now we have to compare all the points in the sub region and make + // sure they exist in the sup region. + + if self.universal_regions.is_universal_region(sup_region) { + // Micro-opt: universal regions contain all points. + return true; } + + inferred_values.contains_points(sup_region, sub_region) } /// Once regions have been propagated, this method is used to see @@ -1007,7 +960,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { longer_fr, shorter_fr, ); - let blame_span = self.blame_span(longer_fr, shorter_fr); + let blame_index = self.blame_constraint(longer_fr, shorter_fr); + let blame_span = self.constraints[blame_index].span; if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { // Shrink `fr` until we find a non-local region (if we do). @@ -1093,9 +1047,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { diag.emit(); } + crate fn why_region_contains_point(&self, fr1: RegionVid, elem: Location) -> Option<Cause> { + // Find some constraint `X: Y` where: + // - `fr1: X` transitively + // - and `Y` is live at `elem` + let index = self.blame_constraint(fr1, elem); + let region_sub = self.constraints[index].sub; + + // then return why `Y` was live at `elem` + self.liveness_constraints.cause(region_sub, elem) + } + /// Tries to finds a good span to blame for the fact that `fr1` /// contains `fr2`. - fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span { + fn blame_constraint(&self, fr1: RegionVid, elem: impl ToElementIndex) -> ConstraintIndex { // Find everything that influenced final value of `fr`. let influenced_fr1 = self.dependencies(fr1); @@ -1108,23 +1073,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { // of dependencies, which doesn't account for the locations of // contraints at all. But it will do for now. let relevant_constraint = self.constraints - .iter() - .filter_map(|constraint| { - if constraint.sub != fr2 { - None - } else { - influenced_fr1[constraint.sup] - .map(|distance| (distance, constraint.span)) - } - }) - .min() // constraining fr1 with fewer hops *ought* to be more obvious - .map(|(_dist, span)| span); + .iter_enumerated() + .filter_map(|(i, constraint)| { + if !self.liveness_constraints.contains(constraint.sub, elem) { + None + } else { + influenced_fr1[constraint.sup] + .map(|distance| (distance, i)) + } + }) + .min() // constraining fr1 with fewer hops *ought* to be more obvious + .map(|(_dist, i)| i); relevant_constraint.unwrap_or_else(|| { bug!( "could not find any constraint to blame for {:?}: {:?}", fr1, - fr2 + elem, ); }) } @@ -1161,16 +1126,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } -impl RegionCausalInfo { - /// Returns the *reason* that the region `r` contains the given point. - pub(super) fn why_region_contains_point<R>(&self, r: R, p: Location) -> Option<Rc<Cause>> - where - R: ToRegionVid, - { - self.inferred_values.cause(r.to_region_vid(), p) - } -} - impl<'tcx> RegionDefinition<'tcx> { fn new(origin: RegionVariableOrigin) -> Self { // Create a new region definition. Note that, for free @@ -1314,31 +1269,3 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi }) } } - -trait CauseExt { - fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause; -} - -impl CauseExt for Rc<Cause> { - /// Creates a derived cause due to an outlives constraint. - fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause { - Cause::Outlives { - original_cause: self.clone(), - constraint_location, - constraint_span, - } - } -} - -impl Cause { - pub(crate) fn root_cause(&self) -> &Cause { - match self { - Cause::LiveVar(..) - | Cause::DropVar(..) - | Cause::LiveOther(..) - | Cause::UniversalRegion(..) => self, - - Cause::Outlives { original_cause, .. } => original_cause.root_cause(), - } - } -} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index d15d85792d9..e914be52db0 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -8,16 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::rc::Rc; +use borrow_check::nll::region_infer::TrackCauses; +use rustc::mir::{BasicBlock, Location, Mir}; +use rustc::ty::RegionVid; use rustc_data_structures::bitvec::SparseBitMatrix; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; -use rustc::mir::{BasicBlock, Location, Mir}; -use rustc::ty::{self, RegionVid}; -use syntax::codemap::Span; +use std::fmt::Debug; +use std::rc::Rc; -use super::{Cause, CauseExt, TrackCauses}; +use super::Cause; /// Maps between the various kinds of elements of a region value to /// the internal indices that w use. @@ -72,11 +73,6 @@ impl RegionValueElements { (0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions)) } - /// Iterates over the `RegionElementIndex` for all points in the CFG. - pub(super) fn all_universal_region_indices(&self) -> impl Iterator<Item = RegionElementIndex> { - (0..self.num_universal_regions).map(move |i| RegionElementIndex::new(i)) - } - /// Converts a particular `RegionElementIndex` to the `RegionElement` it represents. pub(super) fn to_element(&self, i: RegionElementIndex) -> RegionElement { debug!("to_element(i={:?})", i); @@ -152,7 +148,7 @@ pub(super) enum RegionElement { UniversalRegion(RegionVid), } -pub(super) trait ToElementIndex { +pub(super) trait ToElementIndex: Debug + Copy { fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex; } @@ -195,7 +191,7 @@ pub(super) struct RegionValues { causes: Option<CauseMap>, } -type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Rc<Cause>>; +type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Cause>; impl RegionValues { /// Creates a new set of "region values" that tracks causal information. @@ -237,11 +233,22 @@ impl RegionValues { /// Adds the given element to the value for the given region. Returns true if /// the element is newly added (i.e., was not already present). - pub(super) fn add<E: ToElementIndex>(&mut self, r: RegionVid, elem: E, cause: &Cause) -> bool { + pub(super) fn add_element<E: ToElementIndex>( + &mut self, + r: RegionVid, + elem: E, + cause: &Cause, + ) -> bool { let i = self.elements.index(elem); self.add_internal(r, i, |_| cause.clone()) } + /// Add all elements in `r_from` to `r_to` (because e.g. `r_to: + /// r_from`). + pub(super) fn add_region(&mut self, r_to: RegionVid, r_from: RegionVid) -> bool { + self.matrix.merge(r_from, r_to) + } + /// Internal method to add an element to a region. /// /// Takes a "lazy" cause -- this function will return the cause, but it will only @@ -254,7 +261,7 @@ impl RegionValues { debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i)); if let Some(causes) = &mut self.causes { - let cause = Rc::new(make_cause(causes)); + let cause = make_cause(causes); causes.insert((r, i), cause); } @@ -266,15 +273,8 @@ impl RegionValues { // #49998: compare using root cause alone to avoid // useless traffic from similar outlives chains. - let overwrite = if ty::tls::with(|tcx| { - tcx.sess.opts.debugging_opts.nll_subminimal_causes - }) { - cause.root_cause() < old_cause.root_cause() - } else { - cause < **old_cause - }; - if overwrite { - *old_cause = Rc::new(cause); + if cause < *old_cause { + *old_cause = cause; return true; } } @@ -283,62 +283,22 @@ impl RegionValues { } } - /// Adds `elem` to `to_region` because of a relation: - /// - /// to_region: from_region @ constraint_location - /// - /// that was added by the cod at `constraint_span`. - pub(super) fn add_due_to_outlives<T: ToElementIndex>( - &mut self, - from_region: RegionVid, - to_region: RegionVid, - elem: T, - constraint_location: Location, - constraint_span: Span, - ) -> bool { - let elem = self.elements.index(elem); - self.add_internal(to_region, elem, |causes| { - causes[&(from_region, elem)].outlives(constraint_location, constraint_span) - }) - } - - /// Adds all the universal regions outlived by `from_region` to - /// `to_region`. - pub(super) fn add_universal_regions_outlived_by( - &mut self, - from_region: RegionVid, - to_region: RegionVid, - constraint_location: Location, - constraint_span: Span, - ) -> bool { - // We could optimize this by improving `SparseBitMatrix::merge` so - // it does not always merge an entire row. That would - // complicate causal tracking though. - debug!( - "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", - from_region, to_region - ); - let mut changed = false; - for elem in self.elements.all_universal_region_indices() { - if self.contains(from_region, elem) { - changed |= self.add_due_to_outlives( - from_region, - to_region, - elem, - constraint_location, - constraint_span, - ); - } - } - changed - } - /// True if the region `r` contains the given element. pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool { let i = self.elements.index(elem); self.matrix.contains(r, i) } + /// True if `sup_region` contains all the CFG points that + /// `sub_region` contains. Ignores universal regions. + pub(super) fn contains_points(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { + // This could be done faster by comparing the bitsets. But I + // am lazy. + self.element_indices_contained_in(sub_region) + .skip_while(|&i| self.elements.to_universal_region(i).is_some()) + .all(|e| self.contains(sup_region, e)) + } + /// Iterate over the value of the region `r`, yielding up element /// indices. You may prefer `universal_regions_outlived_by` or /// `elements_contained_in`. @@ -444,7 +404,7 @@ impl RegionValues { /// /// Returns None if cause tracking is disabled or `elem` is not /// actually found in `r`. - pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Rc<Cause>> { + pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Cause> { let index = self.elements.index(elem); if let Some(causes) = &self.causes { causes.get(&(r, index)).cloned() diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index b01b3542136..4739c0e92ed 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -21,7 +21,7 @@ use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::hir::map::blocks::FnLikeNode; use rustc::middle::region; use rustc::infer::InferCtxt; -use rustc::ty::layout::IntegerExt; +use rustc::ty::layout::{IntegerExt, Size}; use rustc::ty::subst::Subst; use rustc::ty::{self, Ty, TyCtxt, layout}; use rustc::ty::subst::Substs; @@ -182,7 +182,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { LitKind::Str(ref s, _) => { let s = s.as_str(); let id = self.tcx.allocate_cached(s.as_bytes()); - let ptr = MemoryPointer::new(id, 0); + let ptr = MemoryPointer::new(id, Size::from_bytes(0)); ConstValue::ByValPair( PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128), @@ -190,7 +190,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { }, LitKind::ByteStr(ref data) => { let id = self.tcx.allocate_cached(data); - let ptr = MemoryPointer::new(id, 0); + let ptr = MemoryPointer::new(id, Size::from_bytes(0)); ConstValue::ByVal(PrimVal::Ptr(ptr)) }, LitKind::Byte(n) => ConstValue::ByVal(PrimVal::Bytes(n as u128)), diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index f930d47dc0b..0c2645b4c5c 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -191,7 +191,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { .interpret_interner .get_alloc(ptr.alloc_id) .unwrap(); - assert_eq!(ptr.offset, 0); + assert_eq!(ptr.offset.bytes(), 0); // FIXME: check length alloc.bytes.iter().map(|b| { &*pattern_arena.alloc(Pattern { diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 749e574ff7a..0368e6595c1 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -22,6 +22,7 @@ use rustc::middle::const_val::ConstVal; use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability}; use rustc::mir::interpret::{PrimVal, GlobalId, ConstValue}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; +use rustc::ty::layout::Size; use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind, RangeEnd}; use rustc::hir::def::{Def, CtorKind}; @@ -1083,7 +1084,7 @@ fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, LitKind::Str(ref s, _) => { let s = s.as_str(); let id = tcx.allocate_cached(s.as_bytes()); - let ptr = MemoryPointer::new(id, 0); + let ptr = MemoryPointer::new(id, Size::from_bytes(0)); ConstValue::ByValPair( PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128), @@ -1091,7 +1092,7 @@ fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, }, LitKind::ByteStr(ref data) => { let id = tcx.allocate_cached(data); - let ptr = MemoryPointer::new(id, 0); + let ptr = MemoryPointer::new(id, Size::from_bytes(0)); ConstValue::ByVal(PrimVal::Ptr(ptr)) }, LitKind::Byte(n) => ConstValue::ByVal(PrimVal::Bytes(n as u128)), diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 002b5eb187d..b5568b83339 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -73,7 +73,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { match dest_ty.sty { // float -> uint TyUint(t) => { - let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); + let width = t.bit_width().unwrap_or(self.memory.pointer_size().bytes() as usize * 8); match fty { FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(bits).to_u128(width).value)), FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(bits).to_u128(width).value)), @@ -81,7 +81,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { }, // float -> int TyInt(t) => { - let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); + let width = t.bit_width().unwrap_or(self.memory.pointer_size().bytes() as usize * 8); match fty { FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(bits).to_i128(width).value)), FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(bits).to_i128(width).value)), diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 7d0c16de0a4..c5143817030 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -95,62 +95,35 @@ pub fn eval_body<'a, 'tcx>( pub fn value_to_const_value<'tcx>( ecx: &EvalContext<'_, '_, 'tcx, CompileTimeEvaluator>, - mut val: Value, + val: Value, ty: Ty<'tcx>, ) -> &'tcx ty::Const<'tcx> { - let result = (|| { - // Convert to ByVal or ByValPair if possible - if let Value::ByRef(ptr, align) = val { - if let Some(read_val) = ecx.try_read_value(ptr, align, ty)? { - val = read_val; + let layout = ecx.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).unwrap(); + match (val, &layout.abi) { + (Value::ByVal(PrimVal::Undef), _) if layout.is_zst() => {}, + (Value::ByRef(..), _) | + (Value::ByVal(_), &layout::Abi::Scalar(_)) | + (Value::ByValPair(..), &layout::Abi::ScalarPair(..)) => {}, + _ => bug!("bad value/layout combo: {:#?}, {:#?}", val, layout), + } + let val = (|| { + match val { + Value::ByVal(val) => Ok(ConstValue::ByVal(val)), + Value::ByValPair(a, b) => Ok(ConstValue::ByValPair(a, b)), + Value::ByRef(ptr, align) => { + let ptr = ptr.primval.to_ptr().unwrap(); + let alloc = ecx.memory.get(ptr.alloc_id)?; + assert!(alloc.align.abi() >= align.abi()); + assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= layout.size.bytes()); + let mut alloc = alloc.clone(); + alloc.align = align; + let alloc = ecx.tcx.intern_const_alloc(alloc); + Ok(ConstValue::ByRef(alloc, ptr.offset)) } } - - let layout = ecx.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).unwrap(); - - if layout.is_zst() { - return Ok(ty::Const::from_const_value( - ecx.tcx.tcx, - ConstValue::ByVal(PrimVal::Undef), - ty)); - } - - let val = match layout.abi { - layout::Abi::Scalar(..) => { - if let Value::ByVal(val) = val { - ConstValue::ByVal(val) - } else { - bug!("expected ByVal value, got {:?}", val); - } - } - layout::Abi::ScalarPair(..) => { - if let Value::ByValPair(a, b) = val { - ConstValue::ByValPair(a, b) - } else { - bug!("expected ByValPair value, got {:?}", val); - } - } - _ => { - if let Value::ByRef(ptr, _) = val { - let ptr = ptr.primval.to_ptr().unwrap(); - assert_eq!(ptr.offset, 0); - let alloc = ecx.memory.get(ptr.alloc_id)?; - assert!(alloc.align.abi() >= layout.align.abi()); - assert!(alloc.bytes.len() as u64 == layout.size.bytes()); - let mut alloc = alloc.clone(); - // The align field is meaningless for values, so just use the layout's align - alloc.align = layout.align; - let alloc = ecx.tcx.intern_const_alloc(alloc); - ConstValue::ByRef(alloc) - } else { - bug!("expected ByRef value, got {:?}", val); - } - }, - }; - Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, ty)) })(); - match result { - Ok(v) => v, + match val { + Ok(val) => ty::Const::from_const_value(ecx.tcx.tcx, val, ty), Err(mut err) => { ecx.report(&mut err, true, None); bug!("miri error occured when converting Value to ConstValue") @@ -182,49 +155,49 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>( ) -> EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)> { debug!("eval_body: {:?}, {:?}", cid, param_env); let tcx = ecx.tcx.tcx; - let mut mir = match mir { - Some(mir) => mir, - None => ecx.load_mir(cid.instance.def)?, - }; - if let Some(index) = cid.promoted { - mir = &mir.promoted[index]; - } - let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?; - assert!(!layout.is_unsized()); - let ptr = ecx.memory.allocate( - layout.size.bytes(), - layout.align, - None, - )?; - let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span); - let mutability = tcx.is_static(cid.instance.def_id()); - let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable { - Mutability::Mutable - } else { - Mutability::Immutable - }; - let cleanup = StackPopCleanup::MarkStatic(mutability); - let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id())); - let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p)); - trace!("const_eval: pushing stack frame for global: {}{}", name, prom); - assert!(mir.arg_count == 0); - ecx.push_stack_frame( - cid.instance, - mir.span, - mir, - Place::from_ptr(ptr, layout.align), - cleanup, - )?; + let mut mir = match mir { + Some(mir) => mir, + None => ecx.load_mir(cid.instance.def)?, + }; + if let Some(index) = cid.promoted { + mir = &mir.promoted[index]; + } + let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?; + assert!(!layout.is_unsized()); + let ptr = ecx.memory.allocate( + layout.size, + layout.align, + None, + )?; + let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span); + let is_static = tcx.is_static(cid.instance.def_id()); + let mutability = if is_static == Some(hir::Mutability::MutMutable) || internally_mutable { + Mutability::Mutable + } else { + Mutability::Immutable + }; + let cleanup = StackPopCleanup::MarkStatic(mutability); + let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id())); + let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p)); + trace!("const_eval: pushing stack frame for global: {}{}", name, prom); + assert!(mir.arg_count == 0); + ecx.push_stack_frame( + cid.instance, + mir.span, + mir, + Place::from_ptr(ptr, layout.align), + cleanup, + )?; - while ecx.step()? {} - let ptr = ptr.into(); - // always try to read the value and report errors - let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? { - Some(val) => val, - // point at the allocation - _ => Value::ByRef(ptr, layout.align), - }; - Ok((value, ptr, layout.ty)) + while ecx.step()? {} + let ptr = ptr.into(); + // always try to read the value and report errors + let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? { + Some(val) if is_static.is_none() => val, + // point at the allocation + _ => Value::ByRef(ptr, layout.align), + }; + Ok((value, ptr, layout.ty)) } pub struct CompileTimeEvaluator; @@ -442,22 +415,37 @@ pub fn const_val_field<'a, 'tcx>( let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); let result = (|| { let value = ecx.const_value_to_value(value, ty)?; - let (field, ty) = match value { - Value::ByValPair(..) | Value::ByVal(_) => - ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"), - Value::ByRef(ptr, align) => { - let place = Place::Ptr { - ptr, - align, - extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), - }; - let layout = ecx.layout_of(ty)?; - let (place, layout) = ecx.place_field(place, field, layout)?; - let (ptr, align) = place.to_ptr_align(); - (Value::ByRef(ptr, align), layout.ty) - } + let layout = ecx.layout_of(ty)?; + let (ptr, align) = match value { + Value::ByRef(ptr, align) => (ptr, align), + Value::ByValPair(..) | Value::ByVal(_) => { + let ptr = ecx.alloc_ptr(ty)?.into(); + ecx.write_value_to_ptr(value, ptr, layout.align, ty)?; + (ptr, layout.align) + }, + }; + let place = Place::Ptr { + ptr, + align, + extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant), }; - Ok(value_to_const_value(&ecx, field, ty)) + let (place, layout) = ecx.place_field(place, field, layout)?; + let (ptr, align) = place.to_ptr_align(); + let mut new_value = Value::ByRef(ptr, align); + new_value = ecx.try_read_by_ref(new_value, layout.ty)?; + use rustc_data_structures::indexed_vec::Idx; + match (value, new_value) { + (Value::ByVal(_), Value::ByRef(..)) | + (Value::ByValPair(..), Value::ByRef(..)) | + (Value::ByVal(_), Value::ByValPair(..)) => bug!( + "field {} of {:?} yielded {:?}", + field.index(), + value, + new_value, + ), + _ => {}, + } + Ok(value_to_const_value(&ecx, new_value, layout.ty)) })(); result.map_err(|err| { let (trace, span) = ecx.generate_stacktrace(None); @@ -482,7 +470,7 @@ pub fn const_variant_index<'a, 'tcx>( let (ptr, align) = match value { Value::ByValPair(..) | Value::ByVal(_) => { let layout = ecx.layout_of(ty)?; - let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?; + let ptr = ecx.memory.allocate(layout.size, layout.align, Some(MemoryKind::Stack))?; let ptr: Pointer = ptr.into(); ecx.write_value_to_ptr(value, ptr, layout.align, ty)?; (ptr, layout.align) @@ -498,7 +486,10 @@ pub fn const_value_to_allocation_provider<'a, 'tcx>( (val, ty): (ConstValue<'tcx>, Ty<'tcx>), ) -> &'tcx Allocation { match val { - ConstValue::ByRef(alloc) => return alloc, + ConstValue::ByRef(alloc, offset) => { + assert_eq!(offset.bytes(), 0); + return alloc; + }, _ => () } let result = || -> EvalResult<'tcx, &'tcx Allocation> { @@ -509,7 +500,7 @@ pub fn const_value_to_allocation_provider<'a, 'tcx>( ()); let value = ecx.const_value_to_value(val, ty)?; let layout = ecx.layout_of(ty)?; - let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?; + let ptr = ecx.memory.allocate(layout.size, layout.align, Some(MemoryKind::Stack))?; ecx.write_value_to_ptr(value, ptr.into(), layout.align, ty)?; let alloc = ecx.memory.get(ptr.alloc_id)?; Ok(tcx.intern_const_alloc(alloc.clone())) @@ -551,8 +542,11 @@ pub fn const_eval_provider<'a, 'tcx>( }; let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env); - res.map(|(val, _, miri_ty)| { - value_to_const_value(&ecx, val, miri_ty) + res.and_then(|(mut val, _, miri_ty)| { + if tcx.is_static(def_id).is_none() { + val = ecx.try_read_by_ref(val, miri_ty)?; + } + Ok(value_to_const_value(&ecx, val, miri_ty)) }).map_err(|mut err| { if tcx.is_static(def_id).is_some() { ecx.report(&mut err, true, None); diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 03137619eda..1b4cdccee76 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -207,8 +207,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let layout = self.layout_of(ty)?; assert!(!layout.is_unsized(), "cannot alloc memory for unsized type"); - let size = layout.size.bytes(); - self.memory.allocate(size, layout.align, Some(MemoryKind::Stack)) + self.memory.allocate(layout.size, layout.align, Some(MemoryKind::Stack)) } pub fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> { @@ -243,10 +242,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M _ty: Ty<'tcx>, ) -> EvalResult<'tcx, Value> { match val { - ConstValue::ByRef(alloc) => { + ConstValue::ByRef(alloc, offset) => { // FIXME: Allocate new AllocId for all constants inside let id = self.memory.allocate_value(alloc.clone(), Some(MemoryKind::Stack))?; - Ok(Value::ByRef(MemoryPointer::new(id, 0).into(), alloc.align)) + Ok(Value::ByRef(MemoryPointer::new(id, offset).into(), alloc.align)) }, ConstValue::ByValPair(a, b) => Ok(Value::ByValPair(a, b)), ConstValue::ByVal(val) => Ok(Value::ByVal(val)), @@ -598,14 +597,14 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M ) } }; - let elem_size = self.layout_of(elem_ty)?.size.bytes(); + let elem_size = self.layout_of(elem_ty)?.size; let value = self.eval_operand(operand)?.value; let (dest, dest_align) = self.force_allocation(dest)?.to_ptr_align(); // FIXME: speed up repeat filling for i in 0..length { - let elem_dest = dest.offset(i * elem_size, &self)?; + let elem_dest = dest.offset(elem_size * i as u64, &self)?; self.write_value_to_ptr(value, elem_dest, dest_align, elem_ty)?; } } @@ -1027,7 +1026,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M .interpret_interner .cache_static(gid.instance.def_id()); let layout = self.layout_of(ty)?; - let ptr = MemoryPointer::new(alloc_id, 0); + let ptr = MemoryPointer::new(alloc_id, Size::from_bytes(0)); return Ok(Value::ByRef(ptr.into(), layout.align)) } let cv = self.const_eval(gid)?; @@ -1195,7 +1194,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } else { let dest_ptr = self.alloc_ptr(dest_ty)?.into(); let layout = self.layout_of(dest_ty)?; - self.memory.copy(src_ptr, align.min(layout.align), dest_ptr, layout.align, layout.size.bytes(), false)?; + self.memory.copy(src_ptr, align.min(layout.align), dest_ptr, layout.align, layout.size, false)?; write_dest(self, Value::ByRef(dest_ptr, layout.align))?; } } else { @@ -1217,7 +1216,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M trace!("write_value_to_ptr: {:#?}, {}, {:#?}", value, dest_ty, layout); match value { Value::ByRef(ptr, align) => { - self.memory.copy(ptr, align.min(layout.align), dest, dest_align.min(layout.align), layout.size.bytes(), false) + self.memory.copy(ptr, align.min(layout.align), dest, dest_align.min(layout.align), layout.size, false) } Value::ByVal(primval) => { let signed = match layout.abi { @@ -1228,7 +1227,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M _ if primval.is_undef() => false, _ => bug!("write_value_to_ptr: invalid ByVal layout: {:#?}", layout) }; - self.memory.write_primval(dest, dest_align, primval, layout.size.bytes(), signed) + self.memory.write_primval(dest, dest_align, primval, layout.size, signed) } Value::ByValPair(a_val, b_val) => { trace!("write_value_to_ptr valpair: {:#?}", layout); @@ -1239,10 +1238,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let (a_size, b_size) = (a.size(&self), b.size(&self)); let a_ptr = dest; let b_offset = a_size.abi_align(b.align(&self)); - let b_ptr = dest.offset(b_offset.bytes(), &self)?.into(); + let b_ptr = dest.offset(b_offset, &self)?.into(); // TODO: What about signedess? - self.memory.write_primval(a_ptr, dest_align, a_val, a_size.bytes(), false)?; - self.memory.write_primval(b_ptr, dest_align, b_val, b_size.bytes(), false) + self.memory.write_primval(a_ptr, dest_align, a_val, a_size, false)?; + self.memory.write_primval(b_ptr, dest_align, b_val, b_size, false) } } } @@ -1257,11 +1256,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M ty::TyInt(int_ty) => { use syntax::ast::IntTy::*; let size = match int_ty { - I8 => 1, - I16 => 2, - I32 => 4, - I64 => 8, - I128 => 16, + I8 => Size::from_bytes(1), + I16 => Size::from_bytes(2), + I32 => Size::from_bytes(4), + I64 => Size::from_bytes(8), + I128 => Size::from_bytes(16), Isize => self.memory.pointer_size(), }; PrimValKind::from_int_size(size) @@ -1270,11 +1269,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M ty::TyUint(uint_ty) => { use syntax::ast::UintTy::*; let size = match uint_ty { - U8 => 1, - U16 => 2, - U32 => 4, - U64 => 8, - U128 => 16, + U8 => Size::from_bytes(1), + U16 => Size::from_bytes(2), + U32 => Size::from_bytes(4), + U64 => Size::from_bytes(8), + U128 => Size::from_bytes(16), Usize => self.memory.pointer_size(), }; PrimValKind::from_uint_size(size) @@ -1297,8 +1296,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M layout::Abi::Scalar(ref scalar) => { use rustc::ty::layout::Primitive::*; match scalar.value { - Int(i, false) => PrimValKind::from_uint_size(i.size().bytes()), - Int(i, true) => PrimValKind::from_int_size(i.size().bytes()), + Int(i, false) => PrimValKind::from_uint_size(i.size()), + Int(i, true) => PrimValKind::from_int_size(i.size()), F32 => PrimValKind::F32, F64 => PrimValKind::F64, Pointer => PrimValKind::Ptr, @@ -1372,7 +1371,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M ) -> EvalResult<'tcx> { match ty.sty { ty::TyBool => { - let val = self.memory.read_primval(ptr, ptr_align, 1)?; + let val = self.memory.read_primval(ptr, ptr_align, Size::from_bytes(1))?; match val { PrimVal::Bytes(0) | PrimVal::Bytes(1) => (), // TODO: This seems a little overeager, should reading at bool type already be insta-UB? @@ -1380,7 +1379,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } } ty::TyChar => { - let c = self.memory.read_primval(ptr, ptr_align, 4)?.to_bytes()? as u32; + let c = self.memory.read_primval(ptr, ptr_align, Size::from_bytes(4))?.to_bytes()? as u32; match ::std::char::from_u32(c) { Some(..) => (), None => return err!(InvalidChar(c as u128)), @@ -1402,7 +1401,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M } if let layout::Abi::Scalar(ref scalar) = self.layout_of(ty)?.abi { - let size = scalar.value.size(self).bytes(); + let size = scalar.value.size(self); self.memory.read_primval(ptr, ptr_align, size)?; } } @@ -1412,6 +1411,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M Ok(()) } + pub fn try_read_by_ref(&self, mut val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + // Convert to ByVal or ByValPair if possible + if let Value::ByRef(ptr, align) = val { + if let Some(read_val) = self.try_read_value(ptr, align, ty)? { + val = read_val; + } + } + Ok(val) + } + pub fn try_read_value(&self, ptr: Pointer, ptr_align: Align, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> { let layout = self.layout_of(ty)?; self.memory.check_align(ptr, ptr_align)?; @@ -1427,7 +1436,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M match layout.abi { layout::Abi::Scalar(..) => { - let primval = self.memory.read_primval(ptr, ptr_align, layout.size.bytes())?; + let primval = self.memory.read_primval(ptr, ptr_align, layout.size)?; Ok(Some(Value::ByVal(primval))) } layout::Abi::ScalarPair(ref a, ref b) => { @@ -1435,9 +1444,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M let (a_size, b_size) = (a.size(self), b.size(self)); let a_ptr = ptr; let b_offset = a_size.abi_align(b.align(self)); - let b_ptr = ptr.offset(b_offset.bytes(), self)?.into(); - let a_val = self.memory.read_primval(a_ptr, ptr_align, a_size.bytes())?; - let b_val = self.memory.read_primval(b_ptr, ptr_align, b_size.bytes())?; + let b_ptr = ptr.offset(b_offset, self)?.into(); + let a_val = self.memory.read_primval(a_ptr, ptr_align, a_size)?; + let b_val = self.memory.read_primval(b_ptr, ptr_align, b_size)?; Ok(Some(Value::ByValPair(a_val, b_val))) } _ => Ok(None), diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 5af0a053e92..a5c94e4fcec 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -7,6 +7,7 @@ use super::{EvalContext, Place, ValTy, Memory}; use rustc::mir; use rustc::ty::{self, Ty}; +use rustc::ty::layout::Size; use syntax::codemap::Span; use syntax::ast::Mutability; @@ -92,7 +93,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn check_locks<'a>( _mem: &Memory<'a, 'mir, 'tcx, Self>, _ptr: MemoryPointer, - _size: u64, + _size: Size, _access: AccessKind, ) -> EvalResult<'tcx> { Ok(()) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index ba1c05deef1..755eaa443b6 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -5,7 +5,7 @@ use rustc::hir::def_id::DefId; use rustc::ty::Instance; use rustc::ty::ParamEnv; use rustc::ty::maps::TyCtxtAt; -use rustc::ty::layout::{self, Align, TargetDataLayout}; +use rustc::ty::layout::{self, Align, TargetDataLayout, Size}; use syntax::ast::Mutability; use rustc::middle::const_val::{ConstVal, ErrKind}; @@ -73,12 +73,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> MemoryPointer { let id = self.tcx.interpret_interner.create_fn_alloc(instance); - MemoryPointer::new(id, 0) + MemoryPointer::new(id, Size::from_bytes(0)) } pub fn allocate_cached(&mut self, bytes: &[u8]) -> MemoryPointer { let id = self.tcx.allocate_cached(bytes); - MemoryPointer::new(id, 0) + MemoryPointer::new(id, Size::from_bytes(0)) } /// kind is `None` for statics @@ -105,24 +105,24 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { /// kind is `None` for statics pub fn allocate( &mut self, - size: u64, + size: Size, align: Align, kind: Option<MemoryKind<M::MemoryKinds>>, ) -> EvalResult<'tcx, MemoryPointer> { let id = self.allocate_value(Allocation::undef(size, align), kind)?; - Ok(MemoryPointer::new(id, 0)) + Ok(MemoryPointer::new(id, Size::from_bytes(0))) } pub fn reallocate( &mut self, ptr: MemoryPointer, - old_size: u64, + old_size: Size, old_align: Align, - new_size: u64, + new_size: Size, new_align: Align, kind: MemoryKind<M::MemoryKinds>, ) -> EvalResult<'tcx, MemoryPointer> { - if ptr.offset != 0 { + if ptr.offset.bytes() != 0 { return err!(ReallocateNonBasePtr); } if self.alloc_map.contains_key(&ptr.alloc_id) { @@ -163,10 +163,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn deallocate( &mut self, ptr: MemoryPointer, - size_and_align: Option<(u64, Align)>, + size_and_align: Option<(Size, Align)>, kind: MemoryKind<M::MemoryKinds>, ) -> EvalResult<'tcx> { - if ptr.offset != 0 { + if ptr.offset.bytes() != 0 { return err!(DeallocateNonBasePtr); } @@ -208,8 +208,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { )); } if let Some((size, align)) = size_and_align { - if size != alloc.bytes.len() as u64 || align != alloc.align { - return err!(IncorrectAllocationInformation(size, alloc.bytes.len(), align.abi(), alloc.align.abi())); + if size.bytes() != alloc.bytes.len() as u64 || align != alloc.align { + return err!(IncorrectAllocationInformation(size, Size::from_bytes(alloc.bytes.len() as u64), align, alloc.align)); } } @@ -218,8 +218,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Ok(()) } - pub fn pointer_size(&self) -> u64 { - self.tcx.data_layout.pointer_size.bytes() + pub fn pointer_size(&self) -> Size { + self.tcx.data_layout.pointer_size } pub fn endianness(&self) -> layout::Endian { @@ -232,10 +232,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { let (offset, alloc_align) = match ptr.into_inner_primval() { PrimVal::Ptr(ptr) => { let alloc = self.get(ptr.alloc_id)?; - (ptr.offset, alloc.align) + (ptr.offset.bytes(), alloc.align) } PrimVal::Bytes(bytes) => { - let v = ((bytes as u128) % (1 << self.pointer_size())) as u64; + let v = ((bytes as u128) % (1 << self.pointer_size().bytes())) as u64; if v == 0 { return err!(InvalidNullPointerUsage); } @@ -247,16 +247,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // Check alignment if alloc_align.abi() < required_align.abi() { return err!(AlignmentCheckFailed { - has: alloc_align.abi(), - required: required_align.abi(), + has: alloc_align, + required: required_align, }); } if offset % required_align.abi() == 0 { Ok(()) } else { + let has = offset % required_align.abi(); err!(AlignmentCheckFailed { - has: offset % required_align.abi(), - required: required_align.abi(), + has: Align::from_bytes(has, has).unwrap(), + required: required_align, }) } } @@ -264,11 +265,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn check_bounds(&self, ptr: MemoryPointer, access: bool) -> EvalResult<'tcx> { let alloc = self.get(ptr.alloc_id)?; let allocation_size = alloc.bytes.len() as u64; - if ptr.offset > allocation_size { + if ptr.offset.bytes() > allocation_size { return err!(PointerOutOfBounds { ptr, access, - allocation_size, + allocation_size: Size::from_bytes(allocation_size), }); } Ok(()) @@ -354,7 +355,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } pub fn get_fn(&self, ptr: MemoryPointer) -> EvalResult<'tcx, Instance<'tcx>> { - if ptr.offset != 0 { + if ptr.offset.bytes() != 0 { return err!(InvalidFunctionPointer); } debug!("reading fn ptr: {}", ptr.alloc_id); @@ -419,15 +420,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { }; for i in 0..(alloc.bytes.len() as u64) { + let i = Size::from_bytes(i); if let Some(&target_id) = alloc.relocations.get(&i) { if allocs_seen.insert(target_id) { allocs_to_print.push_back(target_id); } relocations.push((i, target_id)); } - if alloc.undef_mask.is_range_defined(i, i + 1) { + if alloc.undef_mask.is_range_defined(i, i + Size::from_bytes(1)) { // this `as usize` is fine, since `i` came from a `usize` - write!(msg, "{:02x} ", alloc.bytes[i as usize]).unwrap(); + write!(msg, "{:02x} ", alloc.bytes[i.bytes() as usize]).unwrap(); } else { msg.push_str("__ "); } @@ -444,11 +446,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { if !relocations.is_empty() { msg.clear(); write!(msg, "{:1$}", "", prefix_len).unwrap(); // Print spaces. - let mut pos = 0; - let relocation_width = (self.pointer_size() - 1) * 3; + let mut pos = Size::from_bytes(0); + let relocation_width = (self.pointer_size().bytes() - 1) * 3; for (i, target_id) in relocations { // this `as usize` is fine, since we can't print more chars than `usize::MAX` - write!(msg, "{:1$}", "", ((i - pos) * 3) as usize).unwrap(); + write!(msg, "{:1$}", "", ((i - pos) * 3).bytes() as usize).unwrap(); let target = format!("({})", target_id); // this `as usize` is fine, since we can't print more chars than `usize::MAX` write!(msg, "└{0:─^1$}┘ ", target, relocation_width as usize).unwrap(); @@ -476,45 +478,45 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { fn get_bytes_unchecked( &self, ptr: MemoryPointer, - size: u64, + size: Size, align: Align, ) -> EvalResult<'tcx, &[u8]> { // Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL self.check_align(ptr.into(), align)?; - if size == 0 { + if size.bytes() == 0 { return Ok(&[]); } M::check_locks(self, ptr, size, AccessKind::Read)?; self.check_bounds(ptr.offset(size, self)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; - assert_eq!(ptr.offset as usize as u64, ptr.offset); - assert_eq!(size as usize as u64, size); - let offset = ptr.offset as usize; - Ok(&alloc.bytes[offset..offset + size as usize]) + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); + assert_eq!(size.bytes() as usize as u64, size.bytes()); + let offset = ptr.offset.bytes() as usize; + Ok(&alloc.bytes[offset..offset + size.bytes() as usize]) } fn get_bytes_unchecked_mut( &mut self, ptr: MemoryPointer, - size: u64, + size: Size, align: Align, ) -> EvalResult<'tcx, &mut [u8]> { // Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL self.check_align(ptr.into(), align)?; - if size == 0 { + if size.bytes() == 0 { return Ok(&mut []); } M::check_locks(self, ptr, size, AccessKind::Write)?; self.check_bounds(ptr.offset(size, &*self)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get_mut(ptr.alloc_id)?; - assert_eq!(ptr.offset as usize as u64, ptr.offset); - assert_eq!(size as usize as u64, size); - let offset = ptr.offset as usize; - Ok(&mut alloc.bytes[offset..offset + size as usize]) + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); + assert_eq!(size.bytes() as usize as u64, size.bytes()); + let offset = ptr.offset.bytes() as usize; + Ok(&mut alloc.bytes[offset..offset + size.bytes() as usize]) } - fn get_bytes(&self, ptr: MemoryPointer, size: u64, align: Align) -> EvalResult<'tcx, &[u8]> { - assert_ne!(size, 0); + fn get_bytes(&self, ptr: MemoryPointer, size: Size, align: Align) -> EvalResult<'tcx, &[u8]> { + assert_ne!(size.bytes(), 0); if self.relocations(ptr, size)?.count() != 0 { return err!(ReadPointerAsBytes); } @@ -525,10 +527,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { fn get_bytes_mut( &mut self, ptr: MemoryPointer, - size: u64, + size: Size, align: Align, ) -> EvalResult<'tcx, &mut [u8]> { - assert_ne!(size, 0); + assert_ne!(size.bytes(), 0); self.clear_relocations(ptr, size)?; self.mark_definedness(ptr.into(), size, true)?; self.get_bytes_unchecked_mut(ptr, size, align) @@ -594,13 +596,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { src_align: Align, dest: Pointer, dest_align: Align, - size: u64, + size: Size, nonoverlapping: bool, ) -> EvalResult<'tcx> { // Empty accesses don't need to be valid pointers, but they should still be aligned self.check_align(src, src_align)?; self.check_align(dest, dest_align)?; - if size == 0 { + if size.bytes() == 0 { return Ok(()); } let src = src.to_ptr()?; @@ -625,7 +627,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and // `dest` could possibly overlap. unsafe { - assert_eq!(size as usize as u64, size); + assert_eq!(size.bytes() as usize as u64, size.bytes()); if src.alloc_id == dest.alloc_id { if nonoverlapping { if (src.offset <= dest.offset && src.offset + size > dest.offset) || @@ -636,9 +638,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { )); } } - ptr::copy(src_bytes, dest_bytes, size as usize); + ptr::copy(src_bytes, dest_bytes, size.bytes() as usize); } else { - ptr::copy_nonoverlapping(src_bytes, dest_bytes, size as usize); + ptr::copy_nonoverlapping(src_bytes, dest_bytes, size.bytes() as usize); } } @@ -651,26 +653,27 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn read_c_str(&self, ptr: MemoryPointer) -> EvalResult<'tcx, &[u8]> { let alloc = self.get(ptr.alloc_id)?; - assert_eq!(ptr.offset as usize as u64, ptr.offset); - let offset = ptr.offset as usize; + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); + let offset = ptr.offset.bytes() as usize; match alloc.bytes[offset..].iter().position(|&c| c == 0) { Some(size) => { - if self.relocations(ptr, (size + 1) as u64)?.count() != 0 { + let p1 = Size::from_bytes((size + 1) as u64); + if self.relocations(ptr, p1)?.count() != 0 { return err!(ReadPointerAsBytes); } - self.check_defined(ptr, (size + 1) as u64)?; - M::check_locks(self, ptr, (size + 1) as u64, AccessKind::Read)?; + self.check_defined(ptr, p1)?; + M::check_locks(self, ptr, p1, AccessKind::Read)?; Ok(&alloc.bytes[offset..offset + size]) } None => err!(UnterminatedCString(ptr)), } } - pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { + pub fn read_bytes(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx, &[u8]> { // Empty accesses don't need to be valid pointers, but they should still be non-NULL let align = Align::from_bytes(1, 1).unwrap(); self.check_align(ptr, align)?; - if size == 0 { + if size.bytes() == 0 { return Ok(&[]); } self.get_bytes(ptr.to_ptr()?, size, align) @@ -683,16 +686,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { if src.is_empty() { return Ok(()); } - let bytes = self.get_bytes_mut(ptr.to_ptr()?, src.len() as u64, align)?; + let bytes = self.get_bytes_mut(ptr.to_ptr()?, Size::from_bytes(src.len() as u64), align)?; bytes.clone_from_slice(src); Ok(()) } - pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> { + pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: Size) -> EvalResult<'tcx> { // Empty accesses don't need to be valid pointers, but they should still be non-NULL let align = Align::from_bytes(1, 1).unwrap(); self.check_align(ptr, align)?; - if count == 0 { + if count.bytes() == 0 { return Ok(()); } let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, align)?; @@ -702,7 +705,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Ok(()) } - pub fn read_primval(&self, ptr: MemoryPointer, ptr_align: Align, size: u64) -> EvalResult<'tcx, PrimVal> { + pub fn read_primval(&self, ptr: MemoryPointer, ptr_align: Align, size: Size) -> EvalResult<'tcx, PrimVal> { self.check_relocation_edges(ptr, size)?; // Make sure we don't read part of a pointer as a pointer let endianness = self.endianness(); let bytes = self.get_bytes_unchecked(ptr, size, ptr_align.min(self.int_align(size)))?; @@ -721,7 +724,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } else { let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { - Some(&alloc_id) => return Ok(PrimVal::Ptr(MemoryPointer::new(alloc_id, bytes as u64))), + Some(&alloc_id) => return Ok(PrimVal::Ptr(MemoryPointer::new(alloc_id, Size::from_bytes(bytes as u64)))), None => {}, } } @@ -733,13 +736,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { self.read_primval(ptr, ptr_align, self.pointer_size()) } - pub fn write_primval(&mut self, ptr: Pointer, ptr_align: Align, val: PrimVal, size: u64, signed: bool) -> EvalResult<'tcx> { + pub fn write_primval(&mut self, ptr: Pointer, ptr_align: Align, val: PrimVal, size: Size, signed: bool) -> EvalResult<'tcx> { let endianness = self.endianness(); let bytes = match val { PrimVal::Ptr(val) => { assert_eq!(size, self.pointer_size()); - val.offset as u128 + val.offset.bytes() as u128 } PrimVal::Bytes(bytes) => bytes, @@ -782,16 +785,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { self.write_primval(ptr.into(), ptr_align, val, ptr_size, false) } - fn int_align(&self, size: u64) -> Align { + fn int_align(&self, size: Size) -> Align { // We assume pointer-sized integers have the same alignment as pointers. // We also assume signed and unsigned integers of the same size have the same alignment. - let ity = match size { + let ity = match size.bytes() { 1 => layout::I8, 2 => layout::I16, 4 => layout::I32, 8 => layout::I64, 16 => layout::I128, - _ => bug!("bad integer size: {}", size), + _ => bug!("bad integer size: {}", size.bytes()), }; ity.align(self) } @@ -802,14 +805,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { fn relocations( &self, ptr: MemoryPointer, - size: u64, - ) -> EvalResult<'tcx, btree_map::Range<u64, AllocId>> { - let start = ptr.offset.saturating_sub(self.pointer_size() - 1); + size: Size, + ) -> EvalResult<'tcx, btree_map::Range<Size, AllocId>> { + let start = ptr.offset.bytes().saturating_sub(self.pointer_size().bytes() - 1); let end = ptr.offset + size; - Ok(self.get(ptr.alloc_id)?.relocations.range(start..end)) + Ok(self.get(ptr.alloc_id)?.relocations.range(Size::from_bytes(start)..end)) } - fn clear_relocations(&mut self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx> { + fn clear_relocations(&mut self, ptr: MemoryPointer, size: Size) -> EvalResult<'tcx> { // Find all relocations overlapping the given range. let keys: Vec<_> = self.relocations(ptr, size)?.map(|(&k, _)| k).collect(); if keys.is_empty() { @@ -841,9 +844,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Ok(()) } - fn check_relocation_edges(&self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx> { - let overlapping_start = self.relocations(ptr, 0)?.count(); - let overlapping_end = self.relocations(ptr.offset(size, self)?, 0)?.count(); + fn check_relocation_edges(&self, ptr: MemoryPointer, size: Size) -> EvalResult<'tcx> { + let overlapping_start = self.relocations(ptr, Size::from_bytes(0))?.count(); + let overlapping_end = self.relocations(ptr.offset(size, self)?, Size::from_bytes(0))?.count(); if overlapping_start + overlapping_end != 0 { return err!(ReadPointerAsBytes); } @@ -858,26 +861,26 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { &mut self, src: MemoryPointer, dest: MemoryPointer, - size: u64, + size: Size, ) -> EvalResult<'tcx> { // The bits have to be saved locally before writing to dest in case src and dest overlap. - assert_eq!(size as usize as u64, size); - let mut v = Vec::with_capacity(size as usize); - for i in 0..size { - let defined = self.get(src.alloc_id)?.undef_mask.get(src.offset + i); + assert_eq!(size.bytes() as usize as u64, size.bytes()); + let mut v = Vec::with_capacity(size.bytes() as usize); + for i in 0..size.bytes() { + let defined = self.get(src.alloc_id)?.undef_mask.get(src.offset + Size::from_bytes(i)); v.push(defined); } for (i, defined) in v.into_iter().enumerate() { self.get_mut(dest.alloc_id)?.undef_mask.set( dest.offset + - i as u64, + Size::from_bytes(i as u64), defined, ); } Ok(()) } - fn check_defined(&self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx> { + fn check_defined(&self, ptr: MemoryPointer, size: Size) -> EvalResult<'tcx> { let alloc = self.get(ptr.alloc_id)?; if !alloc.undef_mask.is_range_defined( ptr.offset, @@ -892,10 +895,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn mark_definedness( &mut self, ptr: Pointer, - size: u64, + size: Size, new_state: bool, ) -> EvalResult<'tcx> { - if size == 0 { + if size.bytes() == 0 { return Ok(()); } let ptr = ptr.to_ptr()?; diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 883b17b8584..6ccbcf07370 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -1,6 +1,6 @@ use rustc::mir; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; +use rustc::ty::layout::{self, Align, LayoutOf, TyLayout, Size}; use rustc_data_structures::indexed_vec::Idx; use rustc::mir::interpret::{GlobalId, Value, PrimVal, EvalResult, Pointer, MemoryPointer}; @@ -210,7 +210,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { }; let alloc = Machine::init_static(self, cid)?; Place::Ptr { - ptr: MemoryPointer::new(alloc, 0).into(), + ptr: MemoryPointer::new(alloc, Size::from_bytes(0)).into(), align: layout.align, extra: PlaceExtra::None, } @@ -267,9 +267,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { base_layout.ty, base_ptr.to_value_with_vtable(tab), )?; - offset.abi_align(align).bytes() + offset.abi_align(align) } - _ => offset.bytes(), + _ => offset, }; let ptr = base_ptr.offset(offset, &self)?; @@ -325,14 +325,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let (base_ptr, align) = base.to_ptr_align(); let (elem_ty, len) = base.elem_ty_and_len(outer_ty, self.tcx.tcx); - let elem_size = self.layout_of(elem_ty)?.size.bytes(); + let elem_size = self.layout_of(elem_ty)?.size; assert!( n < len, "Tried to access element {} of array/slice with length {}", n, len ); - let ptr = base_ptr.offset(n * elem_size, &*self)?; + let ptr = base_ptr.offset(elem_size * n, &*self)?; Ok(Place::Ptr { ptr, align, @@ -401,7 +401,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let (base_ptr, align) = base.to_ptr_align(); let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx); - let elem_size = self.layout_of(elem_ty)?.size.bytes(); + let elem_size = self.layout_of(elem_ty)?.size; assert!(n >= min_length as u64); let index = if from_end { @@ -410,7 +410,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { u64::from(offset) }; - let ptr = base_ptr.offset(index * elem_size, &self)?; + let ptr = base_ptr.offset(elem_size * index, &self)?; Ok(Place::Ptr { ptr, align, extra: PlaceExtra::None }) } @@ -420,9 +420,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { let (base_ptr, align) = base.to_ptr_align(); let (elem_ty, n) = base.elem_ty_and_len(base_ty, self.tcx.tcx); - let elem_size = self.layout_of(elem_ty)?.size.bytes(); + let elem_size = self.layout_of(elem_ty)?.size; assert!(u64::from(from) <= n - u64::from(to)); - let ptr = base_ptr.offset(u64::from(from) * elem_size, &self)?; + let ptr = base_ptr.offset(elem_size * u64::from(from), &self)?; // sublicing arrays produces arrays let extra = if self.type_is_sized(base_ty) { PlaceExtra::None diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index c61487f106b..c5b823ca87b 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -341,7 +341,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { Value::ByRef(ptr, align) => { for (i, arg_local) in arg_locals.enumerate() { let field = layout.field(&self, i)?; - let offset = layout.fields.offset(i).bytes(); + let offset = layout.fields.offset(i); let arg = Value::ByRef(ptr.offset(offset, &self)?, align.min(field.align)); let dest = diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index ecced1b8168..3bf9453fb51 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -24,17 +24,15 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(decl_macro)] -#![cfg_attr(stage0, feature(dyn_trait))] #![feature(fs_read_write)] #![feature(macro_vis_matcher)] #![feature(exhaustive_patterns)] #![feature(range_contains)] #![feature(rustc_diagnostic_macros)] -#![feature(inclusive_range_methods)] #![feature(crate_visibility_modifier)] #![feature(never_type)] #![feature(specialization)] -#![cfg_attr(stage0, feature(try_trait))] +#![feature(try_trait)] extern crate arena; #[macro_use] @@ -54,16 +52,6 @@ extern crate log_settings; extern crate rustc_apfloat; extern crate byteorder; -#[cfg(stage0)] -macro_rules! do_catch { - ($t:expr) => { (|| ::std::ops::Try::from_ok($t) )() } -} - -#[cfg(not(stage0))] -macro_rules! do_catch { - ($t:expr) => { do catch { $t } } -} - mod diagnostics; mod borrow_check; diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index c62eb1cf185..100edd6b4ff 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -1249,7 +1249,7 @@ fn collect_const<'a, 'tcx>( ConstVal::Value(ConstValue::ByValPair(PrimVal::Ptr(ptr), _)) | ConstVal::Value(ConstValue::ByVal(PrimVal::Ptr(ptr))) => collect_miri(tcx, ptr.alloc_id, output), - ConstVal::Value(ConstValue::ByRef(alloc)) => { + ConstVal::Value(ConstValue::ByRef(alloc, _offset)) => { for &id in alloc.relocations.values() { collect_miri(tcx, id, output); } diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index b732eeb624c..5af19ab3646 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -25,14 +25,12 @@ use rustc::mir::*; use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor}; use rustc::mir::traversal::ReversePostorder; -use rustc::ty::TyCtxt; +use rustc::ty::{self, TyCtxt}; use syntax_pos::Span; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; -use std::iter; -use std::mem; -use std::usize; +use std::{cmp, iter, mem, usize}; /// State of a temporary during collection and promotion. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -150,9 +148,11 @@ pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Local, T } struct Promoter<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, source: &'a mut Mir<'tcx>, promoted: Mir<'tcx>, temps: &'a mut IndexVec<Local, TempState>, + extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>, /// If true, all nested temps are also kept in the /// source MIR, not moved to the promoted MIR. @@ -288,38 +288,90 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { } fn promote_candidate(mut self, candidate: Candidate) { - let span = self.promoted.span; - let new_operand = Operand::Constant(box Constant { - span, - ty: self.promoted.return_ty(), - literal: Literal::Promoted { + let mut rvalue = { + let promoted = &mut self.promoted; + let literal = Literal::Promoted { index: Promoted::new(self.source.promoted.len()) - } - }); - let mut rvalue = match candidate { - Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { - let ref mut statement = self.source[bb].statements[stmt_idx]; - match statement.kind { - StatementKind::Assign(_, ref mut rvalue) => { - mem::replace(rvalue, Rvalue::Use(new_operand)) + }; + let operand = |ty, span| { + promoted.span = span; + promoted.local_decls[RETURN_PLACE] = + LocalDecl::new_return_place(ty, span); + Operand::Constant(box Constant { + span, + ty, + literal + }) + }; + let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut(); + match candidate { + Candidate::Ref(loc) => { + let ref mut statement = blocks[loc.block].statements[loc.statement_index]; + match statement.kind { + StatementKind::Assign(_, Rvalue::Ref(r, bk, ref mut place)) => { + // Find the underlying local for this (necessarilly interior) borrow. + // HACK(eddyb) using a recursive function because of mutable borrows. + fn interior_base<'a, 'tcx>(place: &'a mut Place<'tcx>) + -> &'a mut Place<'tcx> { + if let Place::Projection(ref mut proj) = *place { + assert_ne!(proj.elem, ProjectionElem::Deref); + return interior_base(&mut proj.base); + } + place + } + let place = interior_base(place); + + let ty = place.ty(local_decls, self.tcx).to_ty(self.tcx); + let ref_ty = self.tcx.mk_ref(r, + ty::TypeAndMut { + ty, + mutbl: bk.to_mutbl_lossy() + } + ); + let span = statement.source_info.span; + + // Create a temp to hold the promoted reference. + // This is because `*r` requires `r` to be a local, + // otherwise we would use the `promoted` directly. + let mut promoted_ref = LocalDecl::new_temp(ref_ty, span); + promoted_ref.source_info = statement.source_info; + let promoted_ref = local_decls.push(promoted_ref); + assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref); + self.extra_statements.push((loc, Statement { + source_info: statement.source_info, + kind: StatementKind::Assign( + Place::Local(promoted_ref), + Rvalue::Use(operand(ref_ty, span)), + ) + })); + let promoted_place = Place::Local(promoted_ref).deref(); + + Rvalue::Ref(r, bk, mem::replace(place, promoted_place)) + } + _ => bug!() } - _ => bug!() } - } - Candidate::Argument { bb, index } => { - match self.source[bb].terminator_mut().kind { - TerminatorKind::Call { ref mut args, .. } => { - Rvalue::Use(mem::replace(&mut args[index], new_operand)) + Candidate::Argument { bb, index } => { + let terminator = blocks[bb].terminator_mut(); + match terminator.kind { + TerminatorKind::Call { ref mut args, .. } => { + let ty = args[index].ty(local_decls, self.tcx); + let span = terminator.source_info.span; + Rvalue::Use(mem::replace(&mut args[index], operand(ty, span))) + } + _ => bug!() } - _ => bug!() } } }; + + assert_eq!(self.new_block(), START_BLOCK); self.visit_rvalue(&mut rvalue, Location { block: BasicBlock::new(0), statement_index: usize::MAX }); + let span = self.promoted.span; self.assign(RETURN_PLACE, rvalue, span); self.source.promoted.push(self.promoted); } @@ -343,43 +395,29 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, candidates: Vec<Candidate>) { // Visit candidates in reverse, in case they're nested. debug!("promote_candidates({:?})", candidates); + + let mut extra_statements = vec![]; for candidate in candidates.into_iter().rev() { - let (span, ty) = match candidate { - Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { - let statement = &mir[bb].statements[stmt_idx]; - let dest = match statement.kind { - StatementKind::Assign(ref dest, _) => dest, - _ => { - span_bug!(statement.source_info.span, - "expected assignment to promote"); - } - }; - if let Place::Local(index) = *dest { - if temps[index] == TempState::PromotedOut { - // Already promoted. - continue; + match candidate { + Candidate::Ref(Location { block, statement_index }) => { + match mir[block].statements[statement_index].kind { + StatementKind::Assign(Place::Local(local), _) => { + if temps[local] == TempState::PromotedOut { + // Already promoted. + continue; + } } + _ => {} } - (statement.source_info.span, dest.ty(mir, tcx).to_ty(tcx)) - } - Candidate::Argument { bb, index } => { - let terminator = mir[bb].terminator(); - let ty = match terminator.kind { - TerminatorKind::Call { ref args, .. } => { - args[index].ty(mir, tcx) - } - _ => { - span_bug!(terminator.source_info.span, - "expected call argument to promote"); - } - }; - (terminator.source_info.span, ty) } - }; + Candidate::Argument { .. } => {} + } + - // Declare return place local - let initial_locals = iter::once(LocalDecl::new_return_place(ty, span)) - .collect(); + // Declare return place local so that `Mir::new` doesn't complain. + let initial_locals = iter::once( + LocalDecl::new_return_place(tcx.types.never, mir.span) + ).collect(); let mut promoter = Promoter { promoted: Mir::new( @@ -393,16 +431,24 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, initial_locals, 0, vec![], - span + mir.span ), + tcx, source: mir, temps: &mut temps, + extra_statements: &mut extra_statements, keep_original: false }; - assert_eq!(promoter.new_block(), START_BLOCK); promoter.promote_candidate(candidate); } + // Insert each of `extra_statements` before its indicated location, which + // has to be done in reverse location order, to not invalidate the rest. + extra_statements.sort_by_key(|&(loc, _)| cmp::Reverse(loc)); + for (loc, statement) in extra_statements { + mir[loc.block].statements.insert(loc.statement_index, statement); + } + // Eliminate assignments to, and drops of promoted temps. let promoted = |index: Local| temps[index] == TempState::PromotedOut; for block in mir.basic_blocks_mut() { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 28ab3d6a857..fd4ba1d7562 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -229,12 +229,12 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } /// Check if a Local with the current qualifications is promotable. - fn can_promote(&mut self) -> bool { + fn can_promote(&self, qualif: Qualif) -> bool { // References to statics are allowed, but only in other statics. if self.mode == Mode::Static || self.mode == Mode::StaticMut { - (self.qualif - Qualif::STATIC_REF).is_empty() + (qualif - Qualif::STATIC_REF).is_empty() } else { - self.qualif.is_empty() + qualif.is_empty() } } @@ -679,24 +679,31 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); + + // Default to forbidding the borrow and/or its promotion, + // due to the potential for direct or interior mutability, + // and only proceed by setting `forbidden_mut` to `false`. + let mut forbidden_mut = true; + if let BorrowKind::Mut { .. } = kind { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - let allow = if self.mode == Mode::StaticMut { + if self.mode == Mode::StaticMut { // Inside a `static mut`, &mut [...] is also allowed. match ty.sty { - ty::TyArray(..) | ty::TySlice(_) => true, - _ => false + ty::TyArray(..) | ty::TySlice(_) => forbidden_mut = false, + _ => {} } } else if let ty::TyArray(_, len) = ty.sty { - len.unwrap_usize(self.tcx) == 0 && - self.mode == Mode::Fn - } else { - false - }; + // FIXME(eddyb) the `self.mode == Mode::Fn` condition + // seems unnecessary, given that this is merely a ZST. + if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn { + forbidden_mut = false; + } + } - if !allow { + if forbidden_mut { self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0017, @@ -722,25 +729,46 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // it means that our "silent insertion of statics" could change // initializer values (very bad). if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) { - // Replace MUTABLE_INTERIOR with NOT_CONST to avoid + // A reference of a MUTABLE_INTERIOR place is instead + // NOT_CONST (see `if forbidden_mut` below), to avoid // duplicate errors (from reborrowing, for example). self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR; - self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { span_err!(self.tcx.sess, self.span, E0492, "cannot borrow a constant which may contain \ interior mutability, create a static instead"); } + } else { + // We allow immutable borrows of frozen data. + forbidden_mut = false; } } - // We might have a candidate for promotion. - let candidate = Candidate::Ref(location); - if self.can_promote() { - // We can only promote direct borrows of temps. + if forbidden_mut { + self.add(Qualif::NOT_CONST); + } else { + // We might have a candidate for promotion. + let candidate = Candidate::Ref(location); + // We can only promote interior borrows of promotable temps. + let mut place = place; + while let Place::Projection(ref proj) = *place { + if proj.elem == ProjectionElem::Deref { + break; + } + place = &proj.base; + } if let Place::Local(local) = *place { if self.mir.local_kind(local) == LocalKind::Temp { - self.promotion_candidates.push(candidate); + if let Some(qualif) = self.temp_qualif[local] { + // `forbidden_mut` is false, so we can safely ignore + // `MUTABLE_INTERIOR` from the local's qualifications. + // This allows borrowing fields which don't have + // `MUTABLE_INTERIOR`, from a type that does, e.g.: + // `let _: &'static _ = &(Cell::new(1), 2).1;` + if self.can_promote(qualif - Qualif::MUTABLE_INTERIOR) { + self.promotion_candidates.push(candidate); + } + } } } } @@ -897,7 +925,7 @@ This does not pose a problem by itself because they can't be accessed directly." } let candidate = Candidate::Argument { bb, index: i }; if is_shuffle && i == 2 { - if this.can_promote() { + if this.can_promote(this.qualif) { this.promotion_candidates.push(candidate); } else { span_err!(this.tcx.sess, this.span, E0526, @@ -913,7 +941,7 @@ This does not pose a problem by itself because they can't be accessed directly." if !constant_arguments.contains(&i) { return } - if this.can_promote() { + if this.can_promote(this.qualif) { this.promotion_candidates.push(candidate); } else { this.tcx.sess.span_err(this.span, diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 9d74ad0830f..9e1ce9b2851 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -137,7 +137,7 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>( ) where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, { - let _: io::Result<()> = do_catch! {{ + let _: io::Result<()> = do catch { let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?; writeln!(file, "// MIR for `{}`", node_path)?; writeln!(file, "// source = {:?}", source)?; @@ -150,14 +150,14 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>( extra_data(PassWhere::BeforeCFG, &mut file)?; write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?; extra_data(PassWhere::AfterCFG, &mut file)?; - }}; + }; if tcx.sess.opts.debugging_opts.dump_mir_graphviz { - let _: io::Result<()> = do_catch! {{ + let _: io::Result<()> = do catch { let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?; write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?; - }}; + }; } } diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 4789e2e50ca..4f239a0868e 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -21,7 +21,6 @@ use rustc::session::Session; use syntax::ast::*; use syntax::attr; use syntax::codemap::Spanned; -use syntax::parse::token; use syntax::symbol::keywords; use syntax::visit::{self, Visitor}; use syntax_pos::Span; @@ -40,14 +39,13 @@ impl<'a> AstValidator<'a> { let valid_names = [keywords::UnderscoreLifetime.name(), keywords::StaticLifetime.name(), keywords::Invalid.name()]; - if !valid_names.contains(&ident.name) && - token::is_reserved_ident(ident.without_first_quote()) { + if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() { self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names"); } } fn check_label(&self, ident: Ident) { - if token::is_reserved_ident(ident.without_first_quote()) { + if ident.without_first_quote().is_reserved() { self.err_handler() .span_err(ident.span, &format!("invalid label name `{}`", ident.name)); } diff --git a/src/librustc_passes/loops.rs b/src/librustc_passes/loops.rs index 2368b1aca69..ac37937509e 100644 --- a/src/librustc_passes/loops.rs +++ b/src/librustc_passes/loops.rs @@ -89,6 +89,8 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { self.with_context(LabeledBlock, |v| v.visit_block(&b)); } hir::ExprBreak(label, ref opt_expr) => { + opt_expr.as_ref().map(|e| self.visit_expr(e)); + if self.require_label_in_labeled_block(e.span, &label, "break") { // If we emitted an error about an unlabeled break in a labeled // block, we don't need any further checking for this break any more diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index 7e3c411c1d2..ea15f4c75b9 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -15,6 +15,7 @@ use rustc::session::Session; use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT, IdentTT}; use syntax::ext::base::MacroExpanderFn; +use syntax::ext::hygiene; use syntax::symbol::Symbol; use syntax::ast; use syntax::feature_gate::AttributeType; @@ -107,7 +108,8 @@ impl<'a> Registry<'a> { def_info: _, allow_internal_unstable, allow_internal_unsafe, - unstable_feature + unstable_feature, + edition, } => { let nid = ast::CRATE_NODE_ID; NormalTT { @@ -115,7 +117,8 @@ impl<'a> Registry<'a> { def_info: Some((nid, self.krate_span)), allow_internal_unstable, allow_internal_unsafe, - unstable_feature + unstable_feature, + edition, } } IdentTT(ext, _, allow_internal_unstable) => { @@ -150,6 +153,7 @@ impl<'a> Registry<'a> { allow_internal_unstable: false, allow_internal_unsafe: false, unstable_feature: None, + edition: hygiene::default_edition(), }); } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index ef5cc958283..d3cc533cd36 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -588,7 +588,8 @@ impl<'a> Resolver<'a> { let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess, &self.session.features_untracked(), - ¯o_def)); + ¯o_def, + self.cstore.crate_edition_untracked(def_id.krate))); self.macro_map.insert(def_id, ext.clone()); ext } diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs index 163f6a64010..590ce168d5d 100644 --- a/src/librustc_resolve/check_unused.rs +++ b/src/librustc_resolve/check_unused.rs @@ -142,6 +142,10 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) { } } + for (id, span) in resolver.unused_labels.iter() { + resolver.session.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label"); + } + let mut visitor = UnusedImportCheckVisitor { resolver, unused_imports: NodeMap(), diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 5a5f5ce2e38..bc5d50491ad 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -58,7 +58,6 @@ use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind}; use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path}; use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind}; use syntax::feature_gate::{feature_err, GateIssue}; -use syntax::parse::token; use syntax::ptr::P; use syntax_pos::{Span, DUMMY_SP, MultiSpan}; @@ -1225,12 +1224,10 @@ enum NameBindingKind<'a> { binding: &'a NameBinding<'a>, directive: &'a ImportDirective<'a>, used: Cell<bool>, - legacy_self_import: bool, }, Ambiguity { b1: &'a NameBinding<'a>, b2: &'a NameBinding<'a>, - legacy: bool, } } @@ -1252,7 +1249,6 @@ struct AmbiguityError<'a> { lexical: bool, b1: &'a NameBinding<'a>, b2: &'a NameBinding<'a>, - legacy: bool, } impl<'a> NameBinding<'a> { @@ -1260,7 +1256,6 @@ 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, } } @@ -1270,7 +1265,6 @@ 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, } } @@ -1474,6 +1468,10 @@ pub struct Resolver<'a> { pub maybe_unused_trait_imports: NodeSet, pub maybe_unused_extern_crates: Vec<(NodeId, Span)>, + /// A list of labels as of yet unused. Labels will be removed from this map when + /// they are used (in a `break` or `continue` statement) + pub unused_labels: FxHashMap<NodeId, Span>, + /// privacy errors are delayed until the end in order to deduplicate them privacy_errors: Vec<PrivacyError<'a>>, /// ambiguity errors are delayed for deduplication @@ -1753,6 +1751,8 @@ impl<'a> Resolver<'a> { maybe_unused_trait_imports: NodeSet(), maybe_unused_extern_crates: Vec::new(), + unused_labels: FxHashMap(), + privacy_errors: Vec::new(), ambiguity_errors: Vec::new(), use_injections: Vec::new(), @@ -1853,27 +1853,20 @@ impl<'a> Resolver<'a> { fn record_use(&mut self, ident: Ident, ns: Namespace, binding: &'a NameBinding<'a>, span: Span) -> bool /* true if an error was reported */ { match binding.kind { - NameBindingKind::Import { directive, binding, ref used, legacy_self_import } + NameBindingKind::Import { directive, binding, ref used } if !used.get() => { used.set(true); directive.used.set(true); - if legacy_self_import { - self.warn_legacy_self_import(directive); - return false; - } self.used_imports.insert((directive.id, ns)); self.add_to_glob_map(directive.id, ident); self.record_use(ident, ns, binding, span) } NameBindingKind::Import { .. } => false, - NameBindingKind::Ambiguity { b1, b2, legacy } => { + NameBindingKind::Ambiguity { b1, b2 } => { self.ambiguity_errors.push(AmbiguityError { - span: span, name: ident.name, lexical: false, b1: b1, b2: b2, legacy, + span, name: ident.name, lexical: false, b1, b2, }); - if legacy { - self.record_use(ident, ns, b1, span); - } - !legacy + true } _ => false } @@ -3274,7 +3267,7 @@ impl<'a> Resolver<'a> { // `$crate::a::b` module = Some(self.resolve_crate_root(ident.span.ctxt(), true)); continue - } else if i == 1 && !token::is_path_segment_keyword(ident) { + } else if i == 1 && !ident.is_path_segment_keyword() { let prev_name = path[0].name; if prev_name == keywords::Extern.name() || prev_name == keywords::CrateRoot.name() && @@ -3695,6 +3688,7 @@ impl<'a> Resolver<'a> { where F: FnOnce(&mut Resolver) { if let Some(label) = label { + self.unused_labels.insert(id, label.ident.span); let def = Def::Label(id); self.with_label_rib(|this| { this.label_ribs.last_mut().unwrap().bindings.insert(label.ident, def); @@ -3743,9 +3737,10 @@ impl<'a> Resolver<'a> { ResolutionError::UndeclaredLabel(&label.ident.name.as_str(), close_match)); } - Some(def @ Def::Label(_)) => { + Some(Def::Label(id)) => { // Since this def is a label, it is never read. - self.record_def(expr.id, PathResolution::new(def)); + self.record_def(expr.id, PathResolution::new(Def::Label(id))); + self.unused_labels.remove(&id); } Some(_) => { span_bug!(expr.span, "label wasn't mapped to a label def!"); @@ -4129,7 +4124,7 @@ impl<'a> Resolver<'a> { self.report_proc_macro_import(krate); let mut reported_spans = FxHashSet(); - for &AmbiguityError { span, name, b1, b2, lexical, legacy } in &self.ambiguity_errors { + for &AmbiguityError { span, name, b1, b2, lexical } in &self.ambiguity_errors { if !reported_spans.insert(span) { continue } let participle = |binding: &NameBinding| { if binding.is_import() { "imported" } else { "defined" } @@ -4145,27 +4140,15 @@ impl<'a> Resolver<'a> { 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.buffer_lint(lint::builtin::LEGACY_IMPORTS, id, span, &msg); - } else { - let mut err = - struct_span_err!(self.session, span, E0659, "`{}` is ambiguous", name); - err.span_note(b1.span, &msg1); - match b2.def() { - Def::Macro(..) if b2.span == DUMMY_SP => - err.note(&format!("`{}` is also a builtin macro", name)), - _ => err.span_note(b2.span, &msg2), - }; - err.note(¬e).emit(); - } + + let mut err = struct_span_err!(self.session, span, E0659, "`{}` is ambiguous", name); + err.span_note(b1.span, &msg1); + match b2.def() { + Def::Macro(..) if b2.span == DUMMY_SP => + err.note(&format!("`{}` is also a builtin macro", name)), + _ => err.span_note(b2.span, &msg2), + }; + err.note(¬e).emit(); } for &PrivacyError(span, name, binding) in &self.privacy_errors { @@ -4316,12 +4299,6 @@ impl<'a> Resolver<'a> { self.name_already_seen.insert(name, span); } - fn warn_legacy_self_import(&self, directive: &'a ImportDirective<'a>) { - let (id, span) = (directive.id, directive.span); - let msg = "`self` no longer imports values"; - self.session.buffer_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg); - } - fn check_proc_macro_attrs(&mut self, attrs: &[ast::Attribute]) { if self.proc_macro_enabled { return; } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 4afc621ad8b..57d7b1fac48 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -24,7 +24,7 @@ use syntax::errors::DiagnosticBuilder; use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator}; use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver}; use syntax::ext::expand::{Expansion, ExpansionKind, Invocation, InvocationKind, find_attr_invoc}; -use syntax::ext::hygiene::{Mark, MarkKind}; +use syntax::ext::hygiene::{self, Mark, MarkKind}; use syntax::ext::placeholders::placeholder; use syntax::ext::tt::macro_rules; use syntax::feature_gate::{self, emit_feature_err, GateIssue}; @@ -328,7 +328,7 @@ impl<'a> base::Resolver for Resolver<'a> { for did in self.unused_macros.iter() { let id_span = match *self.macro_map[did] { SyntaxExtension::NormalTT { def_info, .. } => def_info, - SyntaxExtension::DeclMacro(.., osp) => osp, + SyntaxExtension::DeclMacro(.., osp, _) => osp, _ => None, }; if let Some((id, span)) = id_span { @@ -371,7 +371,7 @@ impl<'a> Resolver<'a> { }; for path in traits { match self.resolve_macro(scope, path, MacroKind::Derive, force) { - Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs) = *ext { + Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs, _) = *ext { if inert_attrs.contains(&attr_name) { // FIXME(jseyfried) Avoid `mem::replace` here. let dummy_item = placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID) @@ -528,7 +528,6 @@ impl<'a> Resolver<'a> { b1: shadower, b2: binding, lexical: true, - legacy: false, }); return potential_illegal_shadower; } @@ -755,7 +754,7 @@ impl<'a> Resolver<'a> { let def_id = self.definitions.local_def_id(item.id); let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess, &self.session.features_untracked(), - item)); + item, hygiene::default_edition())); self.macro_map.insert(def_id, ext); let def = match item.node { ast::ItemKind::MacroDef(ref def) => def, _ => unreachable!() }; @@ -803,14 +802,15 @@ impl<'a> Resolver<'a> { match *ext { // If `ext` is a procedural macro, check if we've already warned about it - AttrProcMacro(_) | ProcMacro(_) => if !self.warned_proc_macros.insert(name) { return; }, + AttrProcMacro(..) | ProcMacro(..) => + if !self.warned_proc_macros.insert(name) { return; }, _ => return, } let warn_msg = match *ext { - AttrProcMacro(_) => "attribute procedural macros cannot be \ - imported with `#[macro_use]`", - ProcMacro(_) => "procedural macros cannot be imported with `#[macro_use]`", + AttrProcMacro(..) => "attribute procedural macros cannot be \ + imported with `#[macro_use]`", + ProcMacro(..) => "procedural macros cannot be imported with `#[macro_use]`", _ => return, }; diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 09c421fba47..cf1fc7675fe 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -27,7 +27,6 @@ use rustc::util::nodemap::{FxHashMap, FxHashSet}; use syntax::ast::{Ident, Name, 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; @@ -179,7 +178,6 @@ impl<'a> Resolver<'a> { lexical: false, b1: binding, b2: shadowed_glob, - legacy: false, }); } } @@ -351,7 +349,6 @@ impl<'a> Resolver<'a> { binding, directive, used: Cell::new(false), - legacy_self_import: false, }, span: directive.span, vis, @@ -400,7 +397,7 @@ impl<'a> Resolver<'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, legacy: false }, + kind: NameBindingKind::Ambiguity { b1, b2 }, vis: if b1.vis.is_at_least(b2.vis, self) { b1.vis } else { b2.vis }, span: b1.span, expansion: Mark::root(), @@ -667,7 +664,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { } else { Some(self.resolve_crate_root(source.span.ctxt().modern(), false)) } - } else if is_extern && !token::is_path_segment_keyword(source) { + } else if is_extern && !source.is_path_segment_keyword() { let crate_id = self.resolver.crate_loader.process_use_extern( source.name, @@ -692,7 +689,6 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { binding, directive, used: Cell::new(false), - legacy_self_import: false, }, vis: directive.vis.get(), span: directive.span, @@ -715,8 +711,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { } PathResult::Failed(span, msg, true) => { let (mut self_path, mut self_result) = (module_path.clone(), None); - let is_special = |ident| token::is_path_segment_keyword(ident) && - ident.name != keywords::CrateRoot.name(); + let is_special = |ident: Ident| ident.is_path_segment_keyword() && + ident.name != keywords::CrateRoot.name(); if !self_path.is_empty() && !is_special(self_path[0]) && !(self_path.len() > 1 && is_special(self_path[1])) { self_path[0].name = keywords::SelfValue.name(); @@ -752,7 +748,6 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { }; let mut all_ns_err = true; - let mut legacy_self_import = None; self.per_ns(|this, ns| if !type_ns_only || ns == TypeNS { if let Ok(binding) = result[ns].get() { all_ns_err = false; @@ -761,30 +756,9 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { Some(this.dummy_binding); } } - } else if let Ok(binding) = this.resolve_ident_in_module(module, - ident, - ns, - false, - false, - directive.span) { - legacy_self_import = Some(directive); - let binding = this.arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Import { - binding, - directive, - used: Cell::new(false), - legacy_self_import: true, - }, - ..*binding - }); - let _ = this.try_define(directive.parent, ident, ns, binding); }); if all_ns_err { - if let Some(directive) = legacy_self_import { - self.warn_legacy_self_import(directive); - return None; - } let mut all_ns_failed = true; self.per_ns(|this, ns| if !type_ns_only || ns == TypeNS { match this.resolve_ident_in_module(module, ident, ns, false, true, span) { @@ -1051,23 +1025,6 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { err.emit(); } } - 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 - })); - } _ => {} } } diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index 6cd8e267ec5..2075400d04f 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -221,7 +221,7 @@ pub enum Endian { } /// Size of a type in bytes. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct Size { raw: u64 } diff --git a/src/librustc_target/lib.rs b/src/librustc_target/lib.rs index 45f2ee13bbd..8f491157439 100644 --- a/src/librustc_target/lib.rs +++ b/src/librustc_target/lib.rs @@ -29,7 +29,6 @@ #![feature(const_fn)] #![feature(fs_read_write)] #![feature(inclusive_range)] -#![feature(inclusive_range_methods)] #![feature(slice_patterns)] #[macro_use] diff --git a/src/librustc_target/spec/linux_musl_base.rs b/src/librustc_target/spec/linux_musl_base.rs index 4594d450c15..293f23eab38 100644 --- a/src/librustc_target/spec/linux_musl_base.rs +++ b/src/librustc_target/spec/linux_musl_base.rs @@ -15,8 +15,7 @@ pub fn opts() -> TargetOptions { // Make sure that the linker/gcc really don't pull in anything, including // default objects, libs, etc. - base.pre_link_args_crt.insert(LinkerFlavor::Gcc, Vec::new()); - base.pre_link_args_crt.get_mut(&LinkerFlavor::Gcc).unwrap().push("-nostdlib".to_string()); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-nostdlib".to_string()); // At least when this was tested, the linker would not add the // `GNU_EH_FRAME` program header to executables generated, which is required @@ -56,11 +55,9 @@ pub fn opts() -> TargetOptions { // // Each target directory for musl has these object files included in it so // they'll be included from there. - base.pre_link_objects_exe_crt.push("crt1.o".to_string()); - base.pre_link_objects_exe_crt.push("crti.o".to_string()); - base.pre_link_objects_exe_crt_sys.push("crtbegin.o".to_string()); - base.post_link_objects_crt_sys.push("crtend.o".to_string()); - base.post_link_objects_crt.push("crtn.o".to_string()); + base.pre_link_objects_exe.push("crt1.o".to_string()); + base.pre_link_objects_exe.push("crti.o".to_string()); + base.post_link_objects.push("crtn.o".to_string()); // These targets statically link libc by default base.crt_static_default = true; diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs index fb20fe9c891..0f870b5b957 100644 --- a/src/librustc_target/spec/mod.rs +++ b/src/librustc_target/spec/mod.rs @@ -422,26 +422,20 @@ pub struct TargetOptions { /// Linker to invoke pub linker: Option<String>, - /// Linker arguments that are passed *before* any user-defined libraries. - pub pre_link_args: LinkArgs, // ... unconditionally - pub pre_link_args_crt: LinkArgs, // ... when linking with a bundled crt - /// Objects to link before all others, all except *_sys found within the + /// Linker arguments that are unconditionally passed *before* any + /// user-defined libraries. + pub pre_link_args: LinkArgs, + /// Objects to link before all others, always found within the /// sysroot folder. - pub pre_link_objects_exe: Vec<String>, // ... when linking an executable, unconditionally - pub pre_link_objects_exe_crt: Vec<String>, // ... when linking an executable with a bundled crt - pub pre_link_objects_exe_crt_sys: Vec<String>, // ... when linking an executable with a bundled - // crt, from the system library search path + pub pre_link_objects_exe: Vec<String>, // ... when linking an executable pub pre_link_objects_dll: Vec<String>, // ... when linking a dylib /// Linker arguments that are unconditionally passed after any /// user-defined but before post_link_objects. Standard platform /// libraries that should be always be linked to, usually go here. pub late_link_args: LinkArgs, - /// Objects to link after all others, all except *_sys found within the + /// Objects to link after all others, always found within the /// sysroot folder. - pub post_link_objects: Vec<String>, // ... unconditionally - pub post_link_objects_crt: Vec<String>, // ... when linking with a bundled crt - pub post_link_objects_crt_sys: Vec<String>, // ... when linking with a bundled crt, from the - // system library search path + pub post_link_objects: Vec<String>, /// Linker arguments that are unconditionally passed *after* any /// user-defined libraries. pub post_link_args: LinkArgs, @@ -641,7 +635,6 @@ impl Default for TargetOptions { is_builtin: false, linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()), pre_link_args: LinkArgs::new(), - pre_link_args_crt: LinkArgs::new(), post_link_args: LinkArgs::new(), asm_args: Vec::new(), cpu: "generic".to_string(), @@ -675,12 +668,8 @@ impl Default for TargetOptions { position_independent_executables: false, relro_level: RelroLevel::None, pre_link_objects_exe: Vec::new(), - pre_link_objects_exe_crt: Vec::new(), - pre_link_objects_exe_crt_sys: Vec::new(), pre_link_objects_dll: Vec::new(), post_link_objects: Vec::new(), - post_link_objects_crt: Vec::new(), - post_link_objects_crt_sys: Vec::new(), late_link_args: LinkArgs::new(), link_env: Vec::new(), archive_format: "gnu".to_string(), @@ -899,15 +888,10 @@ impl Target { key!(is_builtin, bool); key!(linker, optional); key!(pre_link_args, link_args); - key!(pre_link_args_crt, link_args); key!(pre_link_objects_exe, list); - key!(pre_link_objects_exe_crt, list); - key!(pre_link_objects_exe_crt_sys, list); key!(pre_link_objects_dll, list); key!(late_link_args, link_args); key!(post_link_objects, list); - key!(post_link_objects_crt, list); - key!(post_link_objects_crt_sys, list); key!(post_link_args, link_args); key!(link_env, env); key!(asm_args, list); @@ -1109,15 +1093,10 @@ impl ToJson for Target { target_option_val!(is_builtin); target_option_val!(linker); target_option_val!(link_args - pre_link_args); - target_option_val!(link_args - pre_link_args_crt); target_option_val!(pre_link_objects_exe); - target_option_val!(pre_link_objects_exe_crt); - target_option_val!(pre_link_objects_exe_crt_sys); target_option_val!(pre_link_objects_dll); target_option_val!(link_args - late_link_args); target_option_val!(post_link_objects); - target_option_val!(post_link_objects_crt); - target_option_val!(post_link_objects_crt_sys); target_option_val!(link_args - post_link_args); target_option_val!(env - link_env); target_option_val!(asm_args); diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs index 8fc00c937e6..a9ac53972e4 100644 --- a/src/librustc_traits/normalize_projection_ty.rs +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -9,8 +9,7 @@ // except according to those terms. use rustc::infer::canonical::{Canonical, QueryResult}; -use rustc::traits::{self, FulfillmentContext, Normalized, ObligationCause, - SelectionContext}; +use rustc::traits::{self, FulfillmentContext, ObligationCause, SelectionContext}; use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult}; use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc_data_structures::sync::Lrc; @@ -37,10 +36,9 @@ crate fn normalize_projection_ty<'tcx>( let fulfill_cx = &mut FulfillmentContext::new(); let selcx = &mut SelectionContext::new(infcx); let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID); - let Normalized { - value: answer, - obligations, - } = traits::normalize_projection_type(selcx, param_env, goal, cause, 0); + let mut obligations = vec![]; + let answer = + traits::normalize_projection_type(selcx, param_env, goal, cause, 0, &mut obligations); fulfill_cx.register_predicate_obligations(infcx, obligations); // Now that we have fulfilled as much as we can, create a solution diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index e1ce6073ce4..4274e5c1e1f 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -129,20 +129,20 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { } let mut selcx = traits::SelectionContext::new(self.fcx); - let normalized = traits::normalize_projection_type(&mut selcx, - self.fcx.param_env, - ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - Symbol::intern("Target"), - ), - cause, - 0); - - debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized); - self.obligations.extend(normalized.obligations); - - Some(self.fcx.resolve_type_vars_if_possible(&normalized.value)) + let normalized_ty = traits::normalize_projection_type(&mut selcx, + self.fcx.param_env, + ty::ProjectionTy::from_ref_and_name( + tcx, + trait_ref, + Symbol::intern("Target"), + ), + cause, + 0, + &mut self.obligations); + + debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty); + + Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty)) } /// Returns the final type, generating an error if it is an diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index feb26e76162..af1f1044edf 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -314,11 +314,6 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32) } - "align_offset" => { - let ptr_ty = tcx.mk_imm_ptr(tcx.mk_nil()); - (0, vec![ptr_ty, tcx.types.usize], tcx.types.usize) - }, - "nontemporal_store" => { (1, vec![ tcx.mk_mut_ptr(param(0)), param(0) ], tcx.mk_nil()) } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 99744d5b4b6..7a572bbbffd 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4519,6 +4519,32 @@ impl Foo for () { ``` "##, +E0646: r##" +It is not possible to define `main` with a where clause. +Erroneous code example: + +```compile_fail,E0646 +fn main() where i32: Copy { // error: main function is not allowed to have + // a where clause +} +``` +"##, + +E0647: r##" +It is not possible to define `start` with a where clause. +Erroneous code example: + +```compile_fail,E0647 +#![feature(start)] + +#[start] +fn start(_: isize, _: *const *const u8) -> isize where (): Copy { + //^ error: start function is not allowed to have a where clause + 0 +} +``` +"##, + E0689: r##" This error indicates that the numeric value for the method being passed exists but the type of the numeric value or binding could not be identified. diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 350b53a406b..ea48e839a7f 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -71,8 +71,6 @@ This API is completely unstable and subject to change. #![allow(non_camel_case_types)] -#![cfg_attr(stage0, feature(dyn_trait))] - #![feature(box_patterns)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] @@ -196,6 +194,12 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, .emit(); return; } + if !generics.where_clause.predicates.is_empty() { + struct_span_err!(tcx.sess, main_span, E0646, + "main function is not allowed to have a where clause") + .emit(); + return; + } } _ => () } @@ -247,14 +251,21 @@ fn check_start_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, match tcx.hir.find(start_id) { Some(hir_map::NodeItem(it)) => { match it.node { - hir::ItemFn(..,ref ps,_) - if !ps.params.is_empty() => { - struct_span_err!(tcx.sess, ps.span, E0132, - "start function is not allowed to have type parameters") - .span_label(ps.span, - "start function cannot have type parameters") - .emit(); - return; + hir::ItemFn(..,ref ps,_) => { + if !ps.params.is_empty() { + struct_span_err!(tcx.sess, ps.span, E0132, + "start function is not allowed to have type parameters") + .span_label(ps.span, + "start function cannot have type parameters") + .emit(); + return; + } + if !ps.where_clause.predicates.is_empty() { + struct_span_err!(tcx.sess, start_span, E0647, + "start function is not allowed to have a where clause") + .emit(); + return; + } } _ => () } diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index d7646ce7bfc..73a7e0e690f 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -35,10 +35,29 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { AdtKind::Enum => Def::Enum, AdtKind::Union => Def::Union, } - _ => panic!("Unexpected type {:?}", def_id), + ty::TyInt(_) | + ty::TyUint(_) | + ty::TyFloat(_) | + ty::TyStr | + ty::TyBool | + ty::TyChar => return self.get_auto_trait_impls(def_id, &move |_: DefId| { + match ty.sty { + ty::TyInt(x) => Def::PrimTy(hir::TyInt(x)), + ty::TyUint(x) => Def::PrimTy(hir::TyUint(x)), + ty::TyFloat(x) => Def::PrimTy(hir::TyFloat(x)), + ty::TyStr => Def::PrimTy(hir::TyStr), + ty::TyBool => Def::PrimTy(hir::TyBool), + ty::TyChar => Def::PrimTy(hir::TyChar), + _ => unreachable!(), + } + }, None), + _ => { + debug!("Unexpected type {:?}", def_id); + return Vec::new() + } }; - self.get_auto_trait_impls(def_id, def_ctor, None) + self.get_auto_trait_impls(def_id, &def_ctor, None) } pub fn get_with_node_id(&self, id: ast::NodeId, name: String) -> Vec<Item> { @@ -52,15 +71,16 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { _ => panic!("Unexpected type {:?} {:?}", item, id), }; - self.get_auto_trait_impls(did, def_ctor, Some(name)) + self.get_auto_trait_impls(did, &def_ctor, Some(name)) } - pub fn get_auto_trait_impls( + pub fn get_auto_trait_impls<F>( &self, def_id: DefId, - def_ctor: fn(DefId) -> Def, + def_ctor: &F, name: Option<String>, - ) -> Vec<Item> { + ) -> Vec<Item> + where F: Fn(DefId) -> Def { if self.cx .tcx .get_attrs(def_id) @@ -68,9 +88,9 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { .has_word("hidden") { debug!( - "get_auto_trait_impls(def_id={:?}, def_ctor={:?}): item has doc('hidden'), \ + "get_auto_trait_impls(def_id={:?}, def_ctor=...): item has doc('hidden'), \ aborting", - def_id, def_ctor + def_id ); return Vec::new(); } @@ -79,8 +99,8 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { let generics = self.cx.tcx.generics_of(def_id); debug!( - "get_auto_trait_impls(def_id={:?}, def_ctor={:?}, generics={:?}", - def_id, def_ctor, generics + "get_auto_trait_impls(def_id={:?}, def_ctor=..., generics={:?}", + def_id, generics ); let auto_traits: Vec<_> = self.cx .send_trait @@ -110,23 +130,24 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { auto_traits } - fn get_auto_trait_impl_for( + fn get_auto_trait_impl_for<F>( &self, def_id: DefId, name: Option<String>, generics: ty::Generics, - def_ctor: fn(DefId) -> Def, + def_ctor: &F, trait_def_id: DefId, - ) -> Option<Item> { + ) -> Option<Item> + where F: Fn(DefId) -> Def { if !self.cx .generated_synthetics .borrow_mut() .insert((def_id, trait_def_id)) { debug!( - "get_auto_trait_impl_for(def_id={:?}, generics={:?}, def_ctor={:?}, \ + "get_auto_trait_impl_for(def_id={:?}, generics={:?}, def_ctor=..., \ trait_def_id={:?}): already generated, aborting", - def_id, generics, def_ctor, trait_def_id + def_id, generics, trait_def_id ); return None; } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index a8f4848bf89..da04068107d 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -302,6 +302,14 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec<clean: for def_id in primitive_impls.iter().filter_map(|&def_id| def_id) { if !def_id.is_local() { build_impl(cx, def_id, &mut impls); + + let auto_impls = get_auto_traits_with_def_id(cx, def_id); + let mut renderinfo = cx.renderinfo.borrow_mut(); + + let new_impls: Vec<clean::Item> = auto_impls.into_iter() + .filter(|i| renderinfo.inlined.insert(i.def_id)).collect(); + + impls.extend(new_impls); } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c233e57a801..6beb64dced1 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -4183,7 +4183,8 @@ pub fn path_to_def(tcx: &TyCtxt, path: &[&str]) -> Option<DefId> { } } -fn get_path_for_type(tcx: TyCtxt, def_id: DefId, def_ctor: fn(DefId) -> Def) -> hir::Path { +fn get_path_for_type<F>(tcx: TyCtxt, def_id: DefId, def_ctor: F) -> hir::Path +where F: Fn(DefId) -> Def { struct AbsolutePathBuffer { names: Vec<String>, } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 3ce95c78a90..1b713a446a0 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -13,8 +13,6 @@ html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/")] -#![cfg_attr(stage0, feature(dyn_trait))] - #![feature(ascii_ctype)] #![feature(rustc_private)] #![feature(box_patterns)] diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index a8578404467..78d3d6d5e60 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -17,7 +17,6 @@ #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; -#[cfg(not(stage0))] #[cfg(not(test))] #[doc(hidden)] #[lang = "oom"] @@ -43,13 +42,6 @@ pub mod __default_lib_allocator { System.alloc(layout) as *mut u8 } - #[cfg(stage0)] - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_oom() -> ! { - super::oom() - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, @@ -74,57 +66,4 @@ pub mod __default_lib_allocator { let layout = Layout::from_size_align_unchecked(size, align); System.alloc_zeroed(layout) as *mut u8 } - - #[cfg(stage0)] - pub mod stage0 { - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_usable_size(_layout: *const u8, - _min: *mut usize, - _max: *mut usize) { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc_excess(_size: usize, - _align: usize, - _excess: *mut usize, - _err: *mut u8) -> *mut u8 { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_realloc_excess(_ptr: *mut u8, - _old_size: usize, - _old_align: usize, - _new_size: usize, - _new_align: usize, - _excess: *mut usize, - _err: *mut u8) -> *mut u8 { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_grow_in_place(_ptr: *mut u8, - _old_size: usize, - _old_align: usize, - _new_size: usize, - _new_align: usize) -> u8 { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_shrink_in_place(_ptr: *mut u8, - _old_size: usize, - _old_align: usize, - _new_size: usize, - _new_align: usize) -> u8 { - unimplemented!() - } - - } } diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 7314d32b020..ae30321f46d 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -19,14 +19,8 @@ #![allow(missing_docs)] #[cfg(not(test))] -#[cfg(stage0)] -use core::num::Float; -#[cfg(not(test))] use intrinsics; #[cfg(not(test))] -#[cfg(stage0)] -use num::FpCategory; -#[cfg(not(test))] use sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] @@ -41,12 +35,8 @@ pub use core::f32::{MIN, MIN_POSITIVE, MAX}; pub use core::f32::consts; #[cfg(not(test))] -#[cfg_attr(stage0, lang = "f32")] -#[cfg_attr(not(stage0), lang = "f32_runtime")] +#[lang = "f32_runtime"] impl f32 { - #[cfg(stage0)] - f32_core_methods!(); - /// Returns the largest integer less than or equal to a number. /// /// # Examples diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 75edba8979f..7950d434b77 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -19,14 +19,8 @@ #![allow(missing_docs)] #[cfg(not(test))] -#[cfg(stage0)] -use core::num::Float; -#[cfg(not(test))] use intrinsics; #[cfg(not(test))] -#[cfg(stage0)] -use num::FpCategory; -#[cfg(not(test))] use sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] @@ -41,12 +35,8 @@ pub use core::f64::{MIN, MIN_POSITIVE, MAX}; pub use core::f64::consts; #[cfg(not(test))] -#[cfg_attr(stage0, lang = "f64")] -#[cfg_attr(not(stage0), lang = "f64_runtime")] +#[lang = "f64_runtime"] impl f64 { - #[cfg(stage0)] - f64_core_methods!(); - /// Returns the largest integer less than or equal to a number. /// /// # Examples diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 9cdc6a21622..f7d06852f27 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -252,7 +252,6 @@ #![feature(collections_range)] #![feature(compiler_builtins_lib)] #![feature(const_fn)] -#![cfg_attr(stage0, feature(core_float))] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] @@ -260,10 +259,8 @@ #![feature(fs_read_write)] #![feature(fixed_size_array)] #![feature(float_from_str_radix)] -#![cfg_attr(stage0, feature(float_internals))] #![feature(fn_traits)] #![feature(fnbox)] -#![cfg_attr(stage0, feature(generic_param_attrs))] #![feature(hashmap_internals)] #![feature(heap_api)] #![feature(int_error_internals)] @@ -319,6 +316,7 @@ #![cfg_attr(test, feature(update_panic_count))] #![cfg_attr(windows, feature(used))] #![feature(doc_alias)] +#![feature(float_internals)] #![default_lib_allocator] @@ -364,11 +362,6 @@ extern crate libc; #[allow(unused_extern_crates)] extern crate unwind; -// compiler-rt intrinsics -#[doc(masked)] -#[cfg(stage0)] -extern crate compiler_builtins; - // During testing, this crate is not actually the "real" std library, but rather // it links to the real std library, which was compiled from this same source // code. So any lang items std defines are conditionally excluded (or else they diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 2b6635ec783..1817726d6a1 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -107,8 +107,7 @@ impl Path { // or starts with something like `self`/`super`/`$crate`/etc. pub fn make_root(&self) -> Option<PathSegment> { if let Some(ident) = self.segments.get(0).map(|seg| seg.ident) { - if ::parse::token::is_path_segment_keyword(ident) && - ident.name != keywords::Crate.name() { + if ident.is_path_segment_keyword() && ident.name != keywords::Crate.name() { return None; } } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 3b76084f2fb..f7d4227977c 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -14,9 +14,10 @@ use ast::{self, Attribute, Name, PatKind, MetaItem}; use attr::HasAttrs; use codemap::{self, CodeMap, Spanned, respan}; use syntax_pos::{Span, MultiSpan, DUMMY_SP}; +use edition::Edition; use errors::{DiagnosticBuilder, DiagnosticId}; use ext::expand::{self, Expansion, Invocation}; -use ext::hygiene::{Mark, SyntaxContext}; +use ext::hygiene::{self, Mark, SyntaxContext}; use fold::{self, Folder}; use parse::{self, parser, DirectoryOwnership}; use parse::token; @@ -586,13 +587,13 @@ pub enum SyntaxExtension { MultiModifier(Box<MultiItemModifier + sync::Sync + sync::Send>), /// A function-like procedural macro. TokenStream -> TokenStream. - ProcMacro(Box<ProcMacro + sync::Sync + sync::Send>), + ProcMacro(Box<ProcMacro + sync::Sync + sync::Send>, Edition), /// An attribute-like procedural macro. TokenStream, TokenStream -> TokenStream. /// The first TokenSteam is the attribute, the second is the annotated item. /// Allows modification of the input items and adding new items, similar to /// MultiModifier, but uses TokenStreams, rather than AST nodes. - AttrProcMacro(Box<AttrProcMacro + sync::Sync + sync::Send>), + AttrProcMacro(Box<AttrProcMacro + sync::Sync + sync::Send>, Edition), /// A normal, function-like syntax extension. /// @@ -608,6 +609,8 @@ pub enum SyntaxExtension { allow_internal_unsafe: bool, /// The macro's feature name if it is unstable, and the stability feature unstable_feature: Option<(Symbol, u32)>, + /// Edition of the crate in which the macro is defined + edition: Edition, }, /// A function-like syntax extension that has an extra ident before @@ -619,9 +622,8 @@ pub enum SyntaxExtension { /// The input is the annotated item. /// Allows generating code to implement a Trait for a given struct /// or enum item. - ProcMacroDerive(Box<MultiItemModifier + - sync::Sync + - sync::Send>, Vec<Symbol> /* inert attribute names */), + ProcMacroDerive(Box<MultiItemModifier + sync::Sync + sync::Send>, + Vec<Symbol> /* inert attribute names */, Edition), /// An attribute-like procedural macro that derives a builtin trait. BuiltinDerive(BuiltinDeriveFn), @@ -629,7 +631,7 @@ pub enum SyntaxExtension { /// A declarative macro, e.g. `macro m() {}`. /// /// The second element is the definition site span. - DeclMacro(Box<TTMacroExpander + sync::Sync + sync::Send>, Option<(ast::NodeId, Span)>), + DeclMacro(Box<TTMacroExpander + sync::Sync + sync::Send>, Option<(ast::NodeId, Span)>, Edition), } impl SyntaxExtension { @@ -660,6 +662,21 @@ impl SyntaxExtension { _ => false, } } + + pub fn edition(&self) -> Edition { + match *self { + SyntaxExtension::NormalTT { edition, .. } | + SyntaxExtension::DeclMacro(.., edition) | + SyntaxExtension::ProcMacro(.., edition) | + SyntaxExtension::AttrProcMacro(.., edition) | + SyntaxExtension::ProcMacroDerive(.., edition) => edition, + // Unstable legacy stuff + SyntaxExtension::IdentTT(..) | + SyntaxExtension::MultiDecorator(..) | + SyntaxExtension::MultiModifier(..) | + SyntaxExtension::BuiltinDerive(..) => hygiene::default_edition(), + } + } } pub type NamedSyntaxExtension = (Name, SyntaxExtension); diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 6bf166dfe95..0b6a7e1c4f4 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -10,7 +10,7 @@ use attr::HasAttrs; use ast; -use codemap::{ExpnInfo, NameAndSpan, ExpnFormat}; +use codemap::{hygiene, ExpnInfo, NameAndSpan, ExpnFormat}; use ext::base::ExtCtxt; use ext::build::AstBuilder; use parse::parser::PathStyle; @@ -65,6 +65,7 @@ pub fn add_derived_markers<T>(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path] span: None, allow_internal_unstable: true, allow_internal_unsafe: false, + edition: hygiene::default_edition(), }, }); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 584b9455a93..ee96963362b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -16,7 +16,7 @@ use config::{is_test_or_bench, StripUnconfigured}; use errors::FatalError; use ext::base::*; use ext::derive::{add_derived_markers, collect_derives}; -use ext::hygiene::{Mark, SyntaxContext}; +use ext::hygiene::{self, Mark, SyntaxContext}; use ext::placeholders::{placeholder, PlaceholderExpander}; use feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err}; use fold; @@ -502,6 +502,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { span: None, allow_internal_unstable: false, allow_internal_unsafe: false, + edition: ext.edition(), } }); @@ -520,7 +521,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { items.push(item); Some(kind.expect_from_annotatables(items)) } - AttrProcMacro(ref mac) => { + AttrProcMacro(ref mac, ..) => { self.gate_proc_macro_attr_item(attr.span, &item); let item_tok = TokenTree::Token(DUMMY_SP, Token::interpolated(match item { Annotatable::Item(item) => token::NtItem(item), @@ -609,7 +610,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { allow_internal_unstable, allow_internal_unsafe, // can't infer this type - unstable_feature: Option<(Symbol, u32)>| { + unstable_feature: Option<(Symbol, u32)>, + edition| { // feature-gate the macro invocation if let Some((feature, issue)) = unstable_feature { @@ -642,15 +644,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> { span: def_site_span, allow_internal_unstable, allow_internal_unsafe, + edition, }, }); Ok(()) }; let opt_expanded = match *ext { - DeclMacro(ref expand, def_span) => { + DeclMacro(ref expand, def_span, edition) => { if let Err(dummy_span) = validate_and_set_expn_info(self, def_span.map(|(_, s)| s), - false, false, None) { + false, false, None, + edition) { dummy_span } else { kind.make_from(expand.expand(self.cx, span, mac.node.stream())) @@ -663,11 +667,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> { allow_internal_unstable, allow_internal_unsafe, unstable_feature, + edition, } => { if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s), allow_internal_unstable, allow_internal_unsafe, - unstable_feature) { + unstable_feature, + edition) { dummy_span } else { kind.make_from(expander.expand(self.cx, span, mac.node.stream())) @@ -688,6 +694,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { span: tt_span, allow_internal_unstable, allow_internal_unsafe: false, + edition: hygiene::default_edition(), } }); @@ -709,7 +716,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { kind.dummy(span) } - ProcMacro(ref expandfun) => { + ProcMacro(ref expandfun, edition) => { if ident.name != keywords::Invalid.name() { let msg = format!("macro {}! expects no ident argument, given '{}'", path, ident); @@ -728,6 +735,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // FIXME probably want to follow macro_rules macros here. allow_internal_unstable: false, allow_internal_unsafe: false, + edition, }, }); @@ -802,11 +810,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { span: None, allow_internal_unstable: false, allow_internal_unsafe: false, + edition: ext.edition(), } }; match *ext { - ProcMacroDerive(ref ext, _) => { + ProcMacroDerive(ref ext, ..) => { invoc.expansion_data.mark.set_expn_info(expn_info); let span = span.with_ctxt(self.cx.backtrace()); let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index e96a0e838cf..d1a7e7aac26 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -10,6 +10,7 @@ use {ast, attr}; use syntax_pos::{Span, DUMMY_SP}; +use edition::Edition; use ext::base::{DummyResult, ExtCtxt, MacResult, SyntaxExtension}; use ext::base::{NormalTT, TTMacroExpander}; use ext::expand::{Expansion, ExpansionKind}; @@ -183,7 +184,8 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt, // Holy self-referential! /// Converts a `macro_rules!` invocation into a syntax extension. -pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> SyntaxExtension { +pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition: Edition) + -> SyntaxExtension { let lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs")); let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs")); @@ -298,10 +300,11 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax def_info: Some((def.id, def.span)), allow_internal_unstable, allow_internal_unsafe, - unstable_feature + unstable_feature, + edition, } } else { - SyntaxExtension::DeclMacro(expander, Some((def.id, def.span))) + SyntaxExtension::DeclMacro(expander, Some((def.id, def.span)), edition) } } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 90af3ba51ec..e9817034569 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -22,7 +22,6 @@ #![feature(unicode_internals)] #![feature(rustc_diagnostic_macros)] #![feature(slice_sort_by_cached_key)] -#![feature(non_exhaustive)] #![feature(const_atomic_usize_new)] #![feature(rustc_attrs)] #![feature(str_escape)] @@ -142,7 +141,6 @@ pub mod codemap; #[macro_use] pub mod config; pub mod entry; -pub mod edition; pub mod feature_gate; pub mod fold; pub mod parse; @@ -150,6 +148,7 @@ pub mod ptr; pub mod show_span; pub mod std_inject; pub mod str; +pub use syntax_pos::edition; pub use syntax_pos::symbol; pub mod test; pub mod tokenstream; diff --git a/src/libsyntax/parse/lexer/comments.rs b/src/libsyntax/parse/lexer/comments.rs index 63aa5d28ce8..672b0b9bbd1 100644 --- a/src/libsyntax/parse/lexer/comments.rs +++ b/src/libsyntax/parse/lexer/comments.rs @@ -238,7 +238,19 @@ fn read_block_comment(rdr: &mut StringReader, debug!(">>> block comment"); let p = rdr.pos; let mut lines: Vec<String> = Vec::new(); - let col = rdr.col; + + // Count the number of chars since the start of the line by rescanning. + let mut src_index = rdr.src_index(rdr.filemap.line_begin_pos()); + let end_src_index = rdr.src_index(rdr.pos); + assert!(src_index <= end_src_index); + let mut n = 0; + while src_index < end_src_index { + let c = char_at(&rdr.src, src_index); + src_index += c.len_utf8(); + n += 1; + } + let col = CharPos(n); + rdr.bump(); rdr.bump(); diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 3e22598043a..bbece1ee5e3 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -44,13 +44,11 @@ pub struct StringReader<'a> { pub next_pos: BytePos, /// The absolute offset within the codemap of the current character pub pos: BytePos, - /// The column of the next character to read - pub col: CharPos, /// The current character (which has been read from self.pos) pub ch: Option<char>, pub filemap: Lrc<syntax_pos::FileMap>, - /// If Some, stop reading the source at this position (inclusive). - pub terminator: Option<BytePos>, + /// Stop reading src at this index. + pub end_src_index: usize, /// Whether to record new-lines and multibyte chars in filemap. /// This is only necessary the first time a filemap is lexed. /// If part of a filemap is being re-lexed, this should be set to false. @@ -61,7 +59,7 @@ pub struct StringReader<'a> { pub fatal_errs: Vec<DiagnosticBuilder<'a>>, // cache a direct reference to the source text, so that we don't have to // retrieve it via `self.filemap.src.as_ref().unwrap()` all the time. - source_text: Lrc<String>, + src: Lrc<String>, /// Stack of open delimiters and their spans. Used for error message. token: token::Token, span: Span, @@ -113,14 +111,7 @@ impl<'a> StringReader<'a> { self.unwrap_or_abort(res) } fn is_eof(&self) -> bool { - if self.ch.is_none() { - return true; - } - - match self.terminator { - Some(t) => self.next_pos > t, - None => false, - } + self.ch.is_none() } /// Return the next token. EFFECT: advances the string_reader. pub fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> { @@ -176,21 +167,20 @@ impl<'a> StringReader<'a> { filemap.name)); } - let source_text = (*filemap.src.as_ref().unwrap()).clone(); + let src = (*filemap.src.as_ref().unwrap()).clone(); StringReader { sess, next_pos: filemap.start_pos, pos: filemap.start_pos, - col: CharPos(0), ch: Some('\n'), filemap, - terminator: None, + end_src_index: src.len(), save_new_lines_and_multibyte: true, // dummy values; not read peek_tok: token::Eof, peek_span: syntax_pos::DUMMY_SP, - source_text, + src, fatal_errs: Vec::new(), token: token::Eof, span: syntax_pos::DUMMY_SP, @@ -222,7 +212,7 @@ impl<'a> StringReader<'a> { // Seek the lexer to the right byte range. sr.save_new_lines_and_multibyte = false; sr.next_pos = span.lo(); - sr.terminator = Some(span.hi()); + sr.end_src_index = sr.src_index(span.hi()); sr.bump(); @@ -326,9 +316,7 @@ impl<'a> StringReader<'a> { /// offending string to the error message fn fatal_span_verbose(&self, from_pos: BytePos, to_pos: BytePos, mut m: String) -> FatalError { m.push_str(": "); - let from = self.byte_offset(from_pos).to_usize(); - let to = self.byte_offset(to_pos).to_usize(); - m.push_str(&self.source_text[from..to]); + m.push_str(&self.src[self.src_index(from_pos)..self.src_index(to_pos)]); self.fatal_span_(from_pos, to_pos, &m[..]) } @@ -354,8 +342,9 @@ impl<'a> StringReader<'a> { Ok(()) } - fn byte_offset(&self, pos: BytePos) -> BytePos { - (pos - self.filemap.start_pos) + #[inline] + fn src_index(&self, pos: BytePos) -> usize { + (pos - self.filemap.start_pos).to_usize() } /// Calls `f` with a string slice of the source text spanning from `start` @@ -386,7 +375,7 @@ impl<'a> StringReader<'a> { fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T where F: FnOnce(&str) -> T { - f(&self.source_text[self.byte_offset(start).to_usize()..self.byte_offset(end).to_usize()]) + f(&self.src[self.src_index(start)..self.src_index(end)]) } /// Converts CRLF to LF in the given string, raising an error on bare CR. @@ -438,47 +427,39 @@ impl<'a> StringReader<'a> { } } - /// Advance the StringReader by one character. If a newline is /// discovered, add it to the FileMap's list of line start offsets. pub fn bump(&mut self) { - let new_pos = self.next_pos; - let new_byte_offset = self.byte_offset(new_pos).to_usize(); - let end = self.terminator.map_or(self.source_text.len(), |t| { - self.byte_offset(t).to_usize() - }); - if new_byte_offset < end { - let old_ch_is_newline = self.ch.unwrap() == '\n'; - let new_ch = char_at(&self.source_text, new_byte_offset); - let new_ch_len = new_ch.len_utf8(); - - self.ch = Some(new_ch); - self.pos = new_pos; - self.next_pos = new_pos + Pos::from_usize(new_ch_len); - if old_ch_is_newline { + let next_src_index = self.src_index(self.next_pos); + if next_src_index < self.end_src_index { + let next_ch = char_at(&self.src, next_src_index); + let next_ch_len = next_ch.len_utf8(); + + if self.ch.unwrap() == '\n' { if self.save_new_lines_and_multibyte { - self.filemap.next_line(self.pos); + self.filemap.next_line(self.next_pos); } - self.col = CharPos(0); - } else { - self.col = self.col + CharPos(1); } - if new_ch_len > 1 { + if next_ch_len > 1 { if self.save_new_lines_and_multibyte { - self.filemap.record_multibyte_char(self.pos, new_ch_len); + self.filemap.record_multibyte_char(self.next_pos, next_ch_len); } } - self.filemap.record_width(self.pos, new_ch); + self.filemap.record_width(self.next_pos, next_ch); + + self.ch = Some(next_ch); + self.pos = self.next_pos; + self.next_pos = self.next_pos + Pos::from_usize(next_ch_len); } else { self.ch = None; - self.pos = new_pos; + self.pos = self.next_pos; } } pub fn nextch(&self) -> Option<char> { - let offset = self.byte_offset(self.next_pos).to_usize(); - if offset < self.source_text.len() { - Some(char_at(&self.source_text, offset)) + let next_src_index = self.src_index(self.next_pos); + if next_src_index < self.end_src_index { + Some(char_at(&self.src, next_src_index)) } else { None } @@ -489,17 +470,15 @@ impl<'a> StringReader<'a> { } pub fn nextnextch(&self) -> Option<char> { - let offset = self.byte_offset(self.next_pos).to_usize(); - let s = &self.source_text[..]; - if offset >= s.len() { - return None; - } - let next = offset + char_at(s, offset).len_utf8(); - if next < s.len() { - Some(char_at(s, next)) - } else { - None + let next_src_index = self.src_index(self.next_pos); + if next_src_index < self.end_src_index { + let next_next_src_index = + next_src_index + char_at(&self.src, next_src_index).len_utf8(); + if next_next_src_index < self.end_src_index { + return Some(char_at(&self.src, next_next_src_index)); + } } + None } pub fn nextnextch_is(&self, c: char) -> bool { @@ -1149,7 +1128,7 @@ impl<'a> StringReader<'a> { return Ok(self.with_str_from(start, |string| { // FIXME: perform NFKC normalization here. (Issue #2253) let ident = self.mk_ident(string); - if is_raw_ident && (token::is_path_segment_keyword(ident) || + if is_raw_ident && (ident.is_path_segment_keyword() || ident.name == keywords::Underscore.name()) { self.fatal_span_(raw_start, self.pos, &format!("`r#{}` is not currently supported.", ident.name) @@ -1359,8 +1338,8 @@ impl<'a> StringReader<'a> { loop { self.bump(); if self.ch_is('\'') { - let start = self.byte_offset(start).to_usize(); - let end = self.byte_offset(self.pos).to_usize(); + let start = self.src_index(start); + let end = self.src_index(self.pos); self.bump(); let span = self.mk_sp(start_with_quote, self.pos); self.sess.span_diagnostic @@ -1369,8 +1348,7 @@ impl<'a> StringReader<'a> { .span_suggestion(span, "if you meant to write a `str` literal, \ use double quotes", - format!("\"{}\"", - &self.source_text[start..end])) + format!("\"{}\"", &self.src[start..end])) .emit(); return Ok(token::Literal(token::Str_(Symbol::intern("??")), None)) } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index a1c056cbb2c..5575614a4d4 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -138,44 +138,6 @@ fn ident_can_begin_type(ident: ast::Ident, is_raw: bool) -> bool { ].contains(&ident.name) } -pub fn is_path_segment_keyword(id: ast::Ident) -> bool { - id.name == keywords::Super.name() || - id.name == keywords::SelfValue.name() || - id.name == keywords::SelfType.name() || - id.name == keywords::Extern.name() || - id.name == keywords::Crate.name() || - id.name == keywords::CrateRoot.name() || - id.name == keywords::DollarCrate.name() -} - -// We see this identifier in a normal identifier position, like variable name or a type. -// How was it written originally? Did it use the raw form? Let's try to guess. -pub fn is_raw_guess(ident: ast::Ident) -> bool { - ident.name != keywords::Invalid.name() && - is_reserved_ident(ident) && !is_path_segment_keyword(ident) -} - -// Returns true for reserved identifiers used internally for elided lifetimes, -// unnamed method parameters, crate root module, error recovery etc. -pub fn is_special_ident(id: ast::Ident) -> bool { - id.name <= keywords::Underscore.name() -} - -/// Returns `true` if the token is a keyword used in the language. -pub fn is_used_keyword(id: ast::Ident) -> bool { - id.name >= keywords::As.name() && id.name <= keywords::While.name() -} - -/// Returns `true` if the token is a keyword reserved for possible future use. -pub fn is_unused_keyword(id: ast::Ident) -> bool { - id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name() -} - -/// Returns `true` if the token is either a special identifier or a keyword. -pub fn is_reserved_ident(id: ast::Ident) -> bool { - is_special_ident(id) || is_used_keyword(id) || is_unused_keyword(id) -} - #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)] pub enum Token { /* Expression-operator symbols. */ @@ -251,7 +213,7 @@ impl Token { /// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary. pub fn from_ast_ident(ident: ast::Ident) -> Token { - Ident(ident, is_raw_guess(ident)) + Ident(ident, ident.is_raw_guess()) } /// Returns `true` if the token starts with '>'. @@ -431,7 +393,7 @@ impl Token { pub fn is_path_segment_keyword(&self) -> bool { match self.ident() { - Some((id, false)) => is_path_segment_keyword(id), + Some((id, false)) => id.is_path_segment_keyword(), _ => false, } } @@ -440,7 +402,7 @@ impl Token { // unnamed method parameters, crate root module, error recovery etc. pub fn is_special_ident(&self) -> bool { match self.ident() { - Some((id, false)) => is_special_ident(id), + Some((id, false)) => id.is_special(), _ => false, } } @@ -448,7 +410,7 @@ impl Token { /// Returns `true` if the token is a keyword used in the language. pub fn is_used_keyword(&self) -> bool { match self.ident() { - Some((id, false)) => is_used_keyword(id), + Some((id, false)) => id.is_used_keyword(), _ => false, } } @@ -456,7 +418,7 @@ impl Token { /// Returns `true` if the token is a keyword reserved for possible future use. pub fn is_unused_keyword(&self) -> bool { match self.ident() { - Some((id, false)) => is_unused_keyword(id), + Some((id, false)) => id.is_unused_keyword(), _ => false, } } @@ -464,7 +426,7 @@ impl Token { /// Returns `true` if the token is either a special identifier or a keyword. pub fn is_reserved_ident(&self) -> bool { match self.ident() { - Some((id, false)) => is_reserved_ident(id), + Some((id, false)) => id.is_reserved(), _ => false, } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index a700799cde5..17f83a09c77 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2374,7 +2374,7 @@ impl<'a> State<'a> { } pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> { - if token::is_raw_guess(ident) { + if ident.is_raw_guess() { self.s.word(&format!("r#{}", ident))?; } else { self.s.word(&ident.name.as_str())?; diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 53dc19ba37d..e9cd7adb9c1 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -14,7 +14,7 @@ use std::cell::Cell; use ext::hygiene::{Mark, SyntaxContext}; use symbol::{Symbol, keywords}; use syntax_pos::{DUMMY_SP, Span}; -use codemap::{ExpnInfo, NameAndSpan, MacroAttribute, dummy_spanned, respan}; +use codemap::{ExpnInfo, NameAndSpan, MacroAttribute, dummy_spanned, hygiene, respan}; use ptr::P; use tokenstream::TokenStream; @@ -30,6 +30,7 @@ fn ignored_span(sp: Span) -> Span { span: None, allow_internal_unstable: true, allow_internal_unsafe: false, + edition: hygiene::default_edition(), } }); sp.with_ctxt(SyntaxContext::empty().apply_mark(mark)) diff --git a/src/libsyntax/str.rs b/src/libsyntax/str.rs index d0f47629b10..281861918fd 100644 --- a/src/libsyntax/str.rs +++ b/src/libsyntax/str.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[inline] pub fn char_at(s: &str, byte: usize) -> char { s[byte..].chars().next().unwrap() } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 1734692f9e7..1dfd48a24c3 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -29,7 +29,7 @@ use entry::{self, EntryPointType}; use ext::base::{ExtCtxt, Resolver}; use ext::build::AstBuilder; use ext::expand::ExpansionConfig; -use ext::hygiene::{Mark, SyntaxContext}; +use ext::hygiene::{self, Mark, SyntaxContext}; use fold::Folder; use feature_gate::Features; use util::move_map::MoveMap; @@ -300,6 +300,7 @@ fn generate_test_harness(sess: &ParseSess, span: None, allow_internal_unstable: true, allow_internal_unsafe: false, + edition: hygiene::default_edition(), } }); diff --git a/src/libsyntax_ext/asm.rs b/src/libsyntax_ext/asm.rs index 369c5b1ff60..dd8f79d20ab 100644 --- a/src/libsyntax_ext/asm.rs +++ b/src/libsyntax_ext/asm.rs @@ -45,17 +45,6 @@ impl State { } } -macro_rules! span_err_if_not_stage0 { - ($cx:expr, $sp:expr, $code:ident, $text:tt) => { - #[cfg(not(stage0))] { - span_err!($cx, $sp, $code, $text) - } - #[cfg(stage0)] { - $cx.span_err($sp, $text) - } - } -} - const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"]; pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, @@ -100,7 +89,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, if asm_str_style.is_some() { // If we already have a string with instructions, // ending up in Asm state again is an error. - span_err_if_not_stage0!(cx, sp, E0660, "malformed inline assembly"); + span_err!(cx, sp, E0660, "malformed inline assembly"); return DummyResult::expr(sp); } // Nested parser, stop before the first colon (see above). @@ -153,7 +142,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, Some(Symbol::intern(&format!("={}", ch.as_str()))) } _ => { - span_err_if_not_stage0!(cx, span, E0661, + span_err!(cx, span, E0661, "output operand constraint lacks '=' or '+'"); None } @@ -179,10 +168,10 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, let (constraint, _str_style) = panictry!(p.parse_str()); if constraint.as_str().starts_with("=") { - span_err_if_not_stage0!(cx, p.prev_span, E0662, + span_err!(cx, p.prev_span, E0662, "input operand constraint contains '='"); } else if constraint.as_str().starts_with("+") { - span_err_if_not_stage0!(cx, p.prev_span, E0663, + span_err!(cx, p.prev_span, E0663, "input operand constraint contains '+'"); } @@ -205,7 +194,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, if OPTIONS.iter().any(|&opt| s == opt) { cx.span_warn(p.prev_span, "expected a clobber, found an option"); } else if s.as_str().starts_with("{") || s.as_str().ends_with("}") { - span_err_if_not_stage0!(cx, p.prev_span, E0664, + span_err!(cx, p.prev_span, E0664, "clobber should not be surrounded by braces"); } diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index f29cc75664d..b22098408a3 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -767,9 +767,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, } if !parser.errors.is_empty() { - let (err, note) = parser.errors.remove(0); - let mut e = cx.ecx.struct_span_err(cx.fmtsp, &format!("invalid format string: {}", err)); - if let Some(note) = note { + let err = parser.errors.remove(0); + let sp = cx.fmtsp.from_inner_byte_pos(err.start, err.end); + let mut e = cx.ecx.struct_span_err(sp, &format!("invalid format string: {}", + err.description)); + e.span_label(sp, err.label + " in format string"); + if let Some(note) = err.note { e.note(¬e); } e.emit(); diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index b6721dd28f3..15fcfac13ad 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -18,7 +18,7 @@ #![feature(decl_macro)] #![feature(str_escape)] -#![cfg_attr(not(stage0), feature(rustc_diagnostic_macros))] +#![feature(rustc_diagnostic_macros)] extern crate fmt_macros; #[macro_use] @@ -29,7 +29,6 @@ extern crate rustc_data_structures; extern crate rustc_errors as errors; extern crate rustc_target; -#[cfg(not(stage0))] mod diagnostics; mod assert; @@ -55,6 +54,7 @@ pub mod proc_macro_impl; use rustc_data_structures::sync::Lrc; use syntax::ast; use syntax::ext::base::{MacroExpanderFn, NormalTT, NamedSyntaxExtension}; +use syntax::ext::hygiene; use syntax::symbol::Symbol; pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, @@ -75,6 +75,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, allow_internal_unstable: false, allow_internal_unsafe: false, unstable_feature: None, + edition: hygiene::default_edition(), }); )* } } @@ -129,7 +130,8 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, def_info: None, allow_internal_unstable: true, allow_internal_unsafe: false, - unstable_feature: None + unstable_feature: None, + edition: hygiene::default_edition(), }); for (name, ext) in user_exts { diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index d684e8b4ffe..3593165023a 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -14,7 +14,7 @@ use errors; use syntax::ast::{self, Ident, NodeId}; use syntax::attr; -use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute, respan}; +use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute, hygiene, respan}; use syntax::ext::base::ExtCtxt; use syntax::ext::build::AstBuilder; use syntax::ext::expand::ExpansionConfig; @@ -369,6 +369,7 @@ fn mk_registrar(cx: &mut ExtCtxt, span: None, allow_internal_unstable: true, allow_internal_unsafe: false, + edition: hygiene::default_edition(), } }); let span = DUMMY_SP.apply_mark(mark); diff --git a/src/libsyntax/edition.rs b/src/libsyntax_pos/edition.rs index c98b54581f3..18446c10996 100644 --- a/src/libsyntax/edition.rs +++ b/src/libsyntax_pos/edition.rs @@ -12,7 +12,7 @@ use std::fmt; use std::str::FromStr; /// The edition of the compiler (RFC 2052) -#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug, RustcEncodable, RustcDecodable)] #[non_exhaustive] pub enum Edition { // editions must be kept in order, newest to oldest diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index be031ea98c9..1365ac396ff 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -17,6 +17,7 @@ use GLOBALS; use Span; +use edition::Edition; use symbol::{Ident, Symbol}; use serialize::{Encodable, Decodable, Encoder, Decoder}; @@ -151,6 +152,7 @@ pub struct HygieneData { syntax_contexts: Vec<SyntaxContextData>, markings: HashMap<(SyntaxContext, Mark), SyntaxContext>, gensym_to_ctxt: HashMap<Symbol, Span>, + default_edition: Edition, } impl HygieneData { @@ -168,6 +170,7 @@ impl HygieneData { }], markings: HashMap::new(), gensym_to_ctxt: HashMap::new(), + default_edition: Edition::Edition2015, } } @@ -176,6 +179,14 @@ impl HygieneData { } } +pub fn default_edition() -> Edition { + HygieneData::with(|data| data.default_edition) +} + +pub fn set_default_edition(edition: Edition) { + HygieneData::with(|data| data.default_edition = edition); +} + pub fn clear_markings() { HygieneData::with(|data| data.markings = HashMap::new()); } @@ -443,6 +454,8 @@ pub struct NameAndSpan { /// Whether the macro is allowed to use `unsafe` internally /// even if the user crate has `#![forbid(unsafe_code)]`. pub allow_internal_unsafe: bool, + /// Edition of the crate in which the macro is defined. + pub edition: Edition, /// The span of the macro definition itself. The macro may not /// have a sensible definition span (e.g. something defined /// completely inside libsyntax) in which case this is None. diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index d30d3d78ca5..17163576901 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -20,6 +20,7 @@ #![feature(const_fn)] #![feature(custom_attribute)] +#![feature(non_exhaustive)] #![feature(optin_builtin_traits)] #![allow(unused_attributes)] #![feature(specialization)] @@ -48,6 +49,7 @@ extern crate serialize as rustc_serialize; // used by deriving extern crate unicode_width; +pub mod edition; pub mod hygiene; pub use hygiene::{Mark, SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind}; @@ -298,6 +300,12 @@ impl Span { self.ctxt().outer().expn_info().map(|i| i.call_site) } + /// Edition of the crate from which this span came. + pub fn edition(self) -> edition::Edition { + self.ctxt().outer().expn_info().map_or_else(|| hygiene::default_edition(), + |einfo| einfo.callee.edition) + } + /// Return the source callee. /// /// Returns None if the supplied span has no expansion trace, @@ -428,6 +436,13 @@ impl Span { ) } + pub fn from_inner_byte_pos(self, start: usize, end: usize) -> Span { + let span = self.data(); + Span::new(span.lo + BytePos::from_usize(start), + span.lo + BytePos::from_usize(end), + span.ctxt) + } + #[inline] pub fn apply_mark(self, mark: Mark) -> Span { let span = self.data(); @@ -971,6 +986,15 @@ impl FileMap { lines.push(pos); } + /// Return the BytePos of the beginning of the current line. + pub fn line_begin_pos(&self) -> BytePos { + let lines = self.lines.borrow(); + match lines.last() { + Some(&line_pos) => line_pos, + None => self.start_pos, + } + } + /// Add externally loaded source. /// If the hash of the input doesn't match or no input is supplied via None, /// it is interpreted as an error and the corresponding enum variant is set. @@ -1047,6 +1071,7 @@ impl FileMap { self.multibyte_chars.borrow_mut().push(mbc); } + #[inline] pub fn record_width(&self, pos: BytePos, ch: char) { let width = match ch { '\t' => diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 2258ed12779..a08f9b2e54a 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -12,6 +12,7 @@ //! allows bidirectional lookup; i.e. given a value, one can easily find the //! type, and vice versa. +use edition::Edition; use hygiene::SyntaxContext; use {Span, DUMMY_SP, GLOBALS}; @@ -318,7 +319,7 @@ macro_rules! declare_keywords {( // NB: leaving holes in the ident table is bad! a different ident will get // interned with the id from the hole, but it will be between the min and max // of the reserved words, and thus tagged as "reserved". -// After modifying this list adjust `is_special_ident`, `is_used_keyword`/`is_unused_keyword`, +// After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`, // this should be rarely necessary though if the keywords are kept in alphabetic order. declare_keywords! { // Special reserved identifiers used internally for elided lifetimes, @@ -383,16 +384,68 @@ declare_keywords! { (53, Virtual, "virtual") (54, Yield, "yield") + // Edition-specific keywords reserved for future use. + (55, Async, "async") // >= 2018 Edition Only + // Special lifetime names - (55, UnderscoreLifetime, "'_") - (56, StaticLifetime, "'static") + (56, UnderscoreLifetime, "'_") + (57, StaticLifetime, "'static") // Weak keywords, have special meaning only in specific contexts. - (57, Auto, "auto") - (58, Catch, "catch") - (59, Default, "default") - (60, Dyn, "dyn") - (61, Union, "union") + (58, Auto, "auto") + (59, Catch, "catch") + (60, Default, "default") + (61, Dyn, "dyn") + (62, Union, "union") +} + +impl Symbol { + fn is_unused_keyword_2018(self) -> bool { + self == keywords::Async.name() + } +} + +impl Ident { + // Returns true for reserved identifiers used internally for elided lifetimes, + // unnamed method parameters, crate root module, error recovery etc. + pub fn is_special(self) -> bool { + self.name <= keywords::Underscore.name() + } + + /// Returns `true` if the token is a keyword used in the language. + pub fn is_used_keyword(self) -> bool { + self.name >= keywords::As.name() && self.name <= keywords::While.name() + } + + /// Returns `true` if the token is a keyword reserved for possible future use. + pub fn is_unused_keyword(self) -> bool { + // Note: `span.edition()` is relatively expensive, don't call it unless necessary. + self.name >= keywords::Abstract.name() && self.name <= keywords::Yield.name() || + self.name.is_unused_keyword_2018() && self.span.edition() == Edition::Edition2018 + } + + /// Returns `true` if the token is either a special identifier or a keyword. + pub fn is_reserved(self) -> bool { + self.is_special() || self.is_used_keyword() || self.is_unused_keyword() + } + + /// A keyword or reserved identifier that can be used as a path segment. + pub fn is_path_segment_keyword(self) -> bool { + self.name == keywords::Super.name() || + self.name == keywords::SelfValue.name() || + self.name == keywords::SelfType.name() || + self.name == keywords::Extern.name() || + self.name == keywords::Crate.name() || + self.name == keywords::CrateRoot.name() || + self.name == keywords::DollarCrate.name() + } + + // We see this identifier in a normal identifier position, like variable name or a type. + // How was it written originally? Did it use the raw form? Let's try to guess. + pub fn is_raw_guess(self) -> bool { + self.name != keywords::Invalid.name() && + self.is_reserved() && !self.is_path_segment_keyword() + } } // If an interner exists, return it. Otherwise, prepare a fresh one. diff --git a/src/libtest/formatters/pretty.rs b/src/libtest/formatters/pretty.rs index 8e5fa00b5f2..f94780682a0 100644 --- a/src/libtest/formatters/pretty.rs +++ b/src/libtest/formatters/pretty.rs @@ -101,7 +101,7 @@ impl<T: Write> PrettyFormatter<T> { for &(ref f, ref stdout) in &state.not_failures { successes.push(f.name.to_string()); if !stdout.is_empty() { - stdouts.push_str(&format!("---- {} stdout ----\n\t", f.name)); + stdouts.push_str(&format!("---- {} stdout ----\n", f.name)); let output = String::from_utf8_lossy(stdout); stdouts.push_str(&output); stdouts.push_str("\n"); @@ -127,7 +127,7 @@ impl<T: Write> PrettyFormatter<T> { for &(ref f, ref stdout) in &state.failures { failures.push(f.name.to_string()); if !stdout.is_empty() { - fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name)); + fail_out.push_str(&format!("---- {} stdout ----\n", f.name)); let output = String::from_utf8_lossy(stdout); fail_out.push_str(&output); fail_out.push_str("\n"); diff --git a/src/libtest/formatters/terse.rs b/src/libtest/formatters/terse.rs index 85286027d69..22a06b9f605 100644 --- a/src/libtest/formatters/terse.rs +++ b/src/libtest/formatters/terse.rs @@ -105,7 +105,7 @@ impl<T: Write> TerseFormatter<T> { for &(ref f, ref stdout) in &state.not_failures { successes.push(f.name.to_string()); if !stdout.is_empty() { - stdouts.push_str(&format!("---- {} stdout ----\n\t", f.name)); + stdouts.push_str(&format!("---- {} stdout ----\n", f.name)); let output = String::from_utf8_lossy(stdout); stdouts.push_str(&output); stdouts.push_str("\n"); @@ -131,7 +131,7 @@ impl<T: Write> TerseFormatter<T> { for &(ref f, ref stdout) in &state.failures { failures.push(f.name.to_string()); if !stdout.is_empty() { - fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name)); + fail_out.push_str(&format!("---- {} stdout ----\n", f.name)); let output = String::from_utf8_lossy(stdout); fail_out.push_str(&output); fail_out.push_str("\n"); diff --git a/src/llvm b/src/llvm -Subproject 1abfd0e562cc8f7a9577d97ee92246699093b95 +Subproject 56c931901cfb85cd6f7ed44c7d7520a8de1edf9 diff --git a/src/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs index 7d5581bb774..8ff401164c1 100644 --- a/src/rtstartup/rsbegin.rs +++ b/src/rtstartup/rsbegin.rs @@ -23,7 +23,7 @@ // of other runtime components (registered via yet another special image section). #![feature(no_core, lang_items, optin_builtin_traits)] -#![crate_type="rlib"] +#![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] @@ -43,7 +43,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { drop_in_place(to_drop); } -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +#[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))] pub mod eh_frames { #[no_mangle] #[link_section = ".eh_frame"] @@ -54,6 +54,21 @@ pub mod eh_frames { // This is defined as `struct object` in $GCC/libgcc/unwind-dw2-fde.h. static mut OBJ: [isize; 6] = [0; 6]; + macro_rules! impl_copy { + ($($t:ty)*) => { + $( + impl ::Copy for $t {} + )* + } + } + + impl_copy! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f32 f64 + bool char + } + // Unwind info registration/deregistration routines. // See the docs of `unwind` module in libstd. extern "C" { @@ -63,14 +78,18 @@ pub mod eh_frames { unsafe fn init() { // register unwind info on module startup - rust_eh_register_frames(&__EH_FRAME_BEGIN__ as *const u8, - &mut OBJ as *mut _ as *mut u8); + rust_eh_register_frames( + &__EH_FRAME_BEGIN__ as *const u8, + &mut OBJ as *mut _ as *mut u8, + ); } unsafe fn uninit() { // unregister on shutdown - rust_eh_unregister_frames(&__EH_FRAME_BEGIN__ as *const u8, - &mut OBJ as *mut _ as *mut u8); + rust_eh_unregister_frames( + &__EH_FRAME_BEGIN__ as *const u8, + &mut OBJ as *mut _ as *mut u8, + ); } // MSVC-specific init/uninit routine registration diff --git a/src/rustc/Cargo.toml b/src/rustc/Cargo.toml index 9986e0b512a..9ccd37a6a45 100644 --- a/src/rustc/Cargo.toml +++ b/src/rustc/Cargo.toml @@ -4,7 +4,7 @@ name = "rustc-main" version = "0.0.0" [[bin]] -name = "rustc" +name = "rustc_binary" path = "rustc.rs" [dependencies] diff --git a/src/rustllvm/llvm-rebuild-trigger b/src/rustllvm/llvm-rebuild-trigger index c3fc3e5452c..5a0292bb6a1 100644 --- a/src/rustllvm/llvm-rebuild-trigger +++ b/src/rustllvm/llvm-rebuild-trigger @@ -1,4 +1,4 @@ # If this file is modified, then llvm will be (optionally) 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. -2018-04-05 +2018-05-18 \ No newline at end of file diff --git a/src/stage0.txt b/src/stage0.txt index a5ad2b315a1..435cfd2f6db 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -12,7 +12,7 @@ # source tarball for a stable release you'll likely see `1.x.0` for rustc and # `0.x.0` for Cargo where they were released on `date`. -date: 2018-04-24 +date: 2018-05-10 rustc: beta cargo: beta diff --git a/src/test/COMPILER_TESTS.md b/src/test/COMPILER_TESTS.md index 29f1e2e5b78..7dabb1bddea 100644 --- a/src/test/COMPILER_TESTS.md +++ b/src/test/COMPILER_TESTS.md @@ -140,13 +140,9 @@ check that the test compiles successfully. ### Editing and updating the reference files If you have changed the compiler's output intentionally, or you are -making a new test, you can use the script `ui/update-references.sh` to -update the references. When you run the test framework, it will report -various errors: in those errors is a command you can use to run the -`ui/update-references.sh` script, which will then copy over the files -from the build directory and use them as the new reference. You can -also just run `ui/update-all-references.sh`. In both cases, you can run -the script with `--help` to get a help message. +making a new test, you can pass `--bless` to the command you used to +run the tests. This will then copy over the files +from the build directory and use them as the new reference. ### Normalization diff --git a/src/test/codegen/function-arguments.rs b/src/test/codegen/function-arguments.rs index 40a9ea5a181..e3fa7a7db39 100644 --- a/src/test/codegen/function-arguments.rs +++ b/src/test/codegen/function-arguments.rs @@ -10,6 +10,7 @@ // compile-flags: -C no-prepopulate-passes // ignore-tidy-linelength +// min-llvm-version 6.0 #![crate_type = "lib"] #![feature(custom_attribute)] @@ -52,16 +53,14 @@ pub fn named_borrow<'r>(_: &'r i32) { pub fn unsafe_borrow(_: &UnsafeInner) { } -// CHECK: @mutable_unsafe_borrow(i16* dereferenceable(2) %arg0) +// CHECK: @mutable_unsafe_borrow(i16* noalias dereferenceable(2) %arg0) // ... unless this is a mutable borrow, those never alias -// ... except that there's this LLVM bug that forces us to not use noalias, see #29485 #[no_mangle] pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) { } -// CHECK: @mutable_borrow(i32* dereferenceable(4) %arg0) +// CHECK: @mutable_borrow(i32* noalias dereferenceable(4) %arg0) // FIXME #25759 This should also have `nocapture` -// ... there's this LLVM bug that forces us to not use noalias, see #29485 #[no_mangle] pub fn mutable_borrow(_: &mut i32) { } @@ -103,9 +102,8 @@ pub fn helper(_: usize) { pub fn slice(_: &[u8]) { } -// CHECK: @mutable_slice([0 x i8]* nonnull %arg0.0, [[USIZE]] %arg0.1) +// CHECK: @mutable_slice([0 x i8]* noalias nonnull %arg0.0, [[USIZE]] %arg0.1) // FIXME #25759 This should also have `nocapture` -// ... there's this LLVM bug that forces us to not use noalias, see #29485 #[no_mangle] pub fn mutable_slice(_: &mut [u8]) { } diff --git a/src/test/codegen/link_section.rs b/src/test/codegen/link_section.rs index 1879002e7f3..415ee6eb7ea 100644 --- a/src/test/codegen/link_section.rs +++ b/src/test/codegen/link_section.rs @@ -12,11 +12,17 @@ #![crate_type = "lib"] -// CHECK: @VAR1 = constant i32 1, section ".test_one" +// CHECK: @VAR1 = constant <{ [4 x i8] }> <{ [4 x i8] c"\01\00\00\00" }>, section ".test_one" #[no_mangle] #[link_section = ".test_one"] +#[cfg(target_endian = "little")] pub static VAR1: u32 = 1; +#[no_mangle] +#[link_section = ".test_one"] +#[cfg(target_endian = "big")] +pub static VAR1: u32 = 0x01000000; + pub enum E { A(u32), B(f32) diff --git a/src/test/compile-fail/array_const_index-0.rs b/src/test/compile-fail/array_const_index-0.rs index 9b9deb4ca8d..91007fcd63a 100644 --- a/src/test/compile-fail/array_const_index-0.rs +++ b/src/test/compile-fail/array_const_index-0.rs @@ -10,9 +10,8 @@ const A: &'static [i32] = &[]; const B: i32 = (&A)[1]; -//~^ ERROR constant evaluation error -//~| index out of bounds: the len is 0 but the index is 1 -//~| WARN this constant cannot be used +//~^ index out of bounds: the len is 0 but the index is 1 +//~| ERROR this constant cannot be used fn main() { let _ = B; diff --git a/src/test/compile-fail/array_const_index-1.rs b/src/test/compile-fail/array_const_index-1.rs index 46feb20cf11..66739d308a7 100644 --- a/src/test/compile-fail/array_const_index-1.rs +++ b/src/test/compile-fail/array_const_index-1.rs @@ -10,9 +10,8 @@ const A: [i32; 0] = []; const B: i32 = A[1]; -//~^ ERROR constant evaluation error -//~| index out of bounds: the len is 0 but the index is 1 -//~| WARN this constant cannot be used +//~^ index out of bounds: the len is 0 but the index is 1 +//~| ERROR this constant cannot be used fn main() { let _ = B; diff --git a/src/test/compile-fail/const-slice-oob.rs b/src/test/compile-fail/const-slice-oob.rs index 7da5a2f17ea..6d51ff30998 100644 --- a/src/test/compile-fail/const-slice-oob.rs +++ b/src/test/compile-fail/const-slice-oob.rs @@ -12,9 +12,8 @@ const FOO: &'static[u32] = &[1, 2, 3]; const BAR: u32 = FOO[5]; -//~^ ERROR constant evaluation error [E0080] -//~| index out of bounds: the len is 3 but the index is 5 -//~| WARN this constant cannot be used +//~^ index out of bounds: the len is 3 but the index is 5 +//~| ERROR this constant cannot be used fn main() { let _ = BAR; diff --git a/src/test/compile-fail/eval-enum.rs b/src/test/compile-fail/eval-enum.rs index 49f76c532df..73156168602 100644 --- a/src/test/compile-fail/eval-enum.rs +++ b/src/test/compile-fail/eval-enum.rs @@ -12,11 +12,11 @@ enum Test { DivZero = 1/0, //~^ attempt to divide by zero //~| ERROR constant evaluation error - //~| WARN constant evaluation error + //~| ERROR constant evaluation error RemZero = 1%0, //~^ attempt to calculate the remainder with a divisor of zero //~| ERROR constant evaluation error - //~| WARN constant evaluation error + //~| ERROR constant evaluation error } fn main() {} diff --git a/src/test/compile-fail/issue-38293.rs b/src/test/compile-fail/issue-38293.rs index bd352b204bd..1867bafa7e3 100644 --- a/src/test/compile-fail/issue-38293.rs +++ b/src/test/compile-fail/issue-38293.rs @@ -10,23 +10,17 @@ // Test that `fn foo::bar::{self}` only imports `bar` in the type namespace. -#![allow(unused)] - mod foo { pub fn f() { } } -use foo::f::{self}; -//~^ ERROR `self` no longer imports values -//~| WARN hard error +use foo::f::{self}; //~ ERROR unresolved import `foo::f` mod bar { pub fn baz() {} pub mod baz {} } use bar::baz::{self}; -//~^ ERROR `self` no longer imports values -//~| WARN hard error fn main() { - baz(); + baz(); //~ ERROR expected function, found module `baz` } diff --git a/src/test/incremental/warnings-reemitted.rs b/src/test/incremental/warnings-reemitted.rs index d50ffff5c1e..1ea436d8ad1 100644 --- a/src/test/incremental/warnings-reemitted.rs +++ b/src/test/incremental/warnings-reemitted.rs @@ -13,6 +13,7 @@ // compile-pass #![allow(warnings)] +#![warn(const_err)] fn main() { 255u8 + 1; //~ WARNING this expression will panic at run-time diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs index e189f2e3b34..15b104f6c2f 100644 --- a/src/test/mir-opt/end_region_destruction_extents_1.rs +++ b/src/test/mir-opt/end_region_destruction_extents_1.rs @@ -130,17 +130,21 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // let mut _7: &'10s S1; // let mut _8: &'10s S1; // let mut _9: S1; +// let mut _10: &'10s S1; +// let mut _11: &'12ds S1; // // bb0: { // StorageLive(_2); // StorageLive(_3); // StorageLive(_4); // StorageLive(_5); -// _5 = promoted[1]; +// _11 = promoted[1]; +// _5 = &'12ds (*_11); // _4 = &'12ds (*_5); // StorageLive(_7); // StorageLive(_8); -// _8 = promoted[0]; +// _10 = promoted[0]; +// _8 = &'10s (*_10); // _7 = &'10s (*_8); // _3 = D1<'12ds, '10s>::{{constructor}}(move _4, move _7); // EndRegion('10s); diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index a31298a0f51..c2a40399efe 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -88,7 +88,8 @@ fn main() { // } // bb9: { // binding1 and guard // StorageLive(_5); -// _5 = &((_2 as Some).0: i32); +// _11 = promoted[0]; +// _5 = &(((*_11) as Some).0: i32); // StorageLive(_8); // _8 = const guard() -> [return: bb10, unwind: bb1]; // } diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs index 153739133ac..62064fa94f2 100644 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -13,6 +13,9 @@ // borrows in `&v[0]` and `&v[1]` each (in theory) have to outlive R3, // but only at a particular point, and hence they wind up including // distinct regions. +// +// FIXME(#43234) -- Well, this used to be true, but we modified NLL +// for the time being to not take location into account. // compile-flags:-Zborrowck=mir -Zverbose // ^^^^^^^^^ force compiler to dump more region information @@ -36,9 +39,9 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#2r | {bb2[0..=1], bb3[0..=1]} +// | '_#2r | {bb2[0..=1], bb3[0..=1], bb8[2..=4]} // ... -// | '_#4r | {bb8[1..=4]} +// | '_#4r | {bb2[1], bb3[0..=1], bb8[1..=4]} // | '_#5r | {bb2[1], bb3[0..=1], bb8[2..=4]} // ... // let mut _2: &'_#5r usize; diff --git a/src/test/run-fail/overflowing-add.rs b/src/test/run-fail/overflowing-add.rs index 250f0726dc9..584701410ef 100644 --- a/src/test/run-fail/overflowing-add.rs +++ b/src/test/run-fail/overflowing-add.rs @@ -11,6 +11,8 @@ // error-pattern:thread 'main' panicked at 'attempt to add with overflow' // compile-flags: -C debug-assertions +#![allow(const_err)] + fn main() { let _x = 200u8 + 200u8 + 200u8; } diff --git a/src/test/run-fail/overflowing-mul.rs b/src/test/run-fail/overflowing-mul.rs index b47d0fc4136..5d6f59e0229 100644 --- a/src/test/run-fail/overflowing-mul.rs +++ b/src/test/run-fail/overflowing-mul.rs @@ -11,6 +11,8 @@ // error-pattern:thread 'main' panicked at 'attempt to multiply with overflow' // compile-flags: -C debug-assertions +#![allow(const_err)] + fn main() { let x = 200u8 * 4; } diff --git a/src/test/run-fail/overflowing-neg.rs b/src/test/run-fail/overflowing-neg.rs index 836d7e37319..e7c518f1286 100644 --- a/src/test/run-fail/overflowing-neg.rs +++ b/src/test/run-fail/overflowing-neg.rs @@ -11,6 +11,8 @@ // error-pattern:thread 'main' panicked at 'attempt to negate with overflow' // compile-flags: -C debug-assertions +#![allow(const_err)] + fn main() { let _x = -std::i8::MIN; } diff --git a/src/test/run-fail/overflowing-sub.rs b/src/test/run-fail/overflowing-sub.rs index f94cb31b168..404921a1af5 100644 --- a/src/test/run-fail/overflowing-sub.rs +++ b/src/test/run-fail/overflowing-sub.rs @@ -11,6 +11,8 @@ // error-pattern:thread 'main' panicked at 'attempt to subtract with overflow' // compile-flags: -C debug-assertions +#![allow(const_err)] + fn main() { let _x = 42u8 - (42u8 + 1); } diff --git a/src/test/run-make-fulldeps/issue-36710/Makefile b/src/test/run-make-fulldeps/issue-36710/Makefile deleted file mode 100644 index 2cb0b4ccf26..00000000000 --- a/src/test/run-make-fulldeps/issue-36710/Makefile +++ /dev/null @@ -1,12 +0,0 @@ --include ../tools.mk - -all: foo - $(call RUN,foo) - -foo: foo.rs $(call NATIVE_STATICLIB,foo) - $(RUSTC) $< -lfoo $(EXTRACXXFLAGS) - -$(TMPDIR)/libfoo.o: foo.cpp - $(call COMPILE_OBJ_CXX,$@,$<) - -.PHONY: all diff --git a/src/test/run-make-fulldeps/tools.mk b/src/test/run-make-fulldeps/tools.mk index 3de358fa500..af1707de6c0 100644 --- a/src/test/run-make-fulldeps/tools.mk +++ b/src/test/run-make-fulldeps/tools.mk @@ -59,14 +59,12 @@ endif ifdef IS_MSVC COMPILE_OBJ = $(CC) -c -Fo:`cygpath -w $(1)` $(2) -COMPILE_OBJ_CXX = $(CXX) -c -Fo:`cygpath -w $(1)` $(2) NATIVE_STATICLIB_FILE = $(1).lib NATIVE_STATICLIB = $(TMPDIR)/$(call NATIVE_STATICLIB_FILE,$(1)) OUT_EXE=-Fe:`cygpath -w $(TMPDIR)/$(call BIN,$(1))` \ -Fo:`cygpath -w $(TMPDIR)/$(1).obj` else COMPILE_OBJ = $(CC) -c -o $(1) $(2) -COMPILE_OBJ_CXX = $(CXX) -c -o $(1) $(2) NATIVE_STATICLIB_FILE = lib$(1).a NATIVE_STATICLIB = $(call STATICLIB,$(1)) OUT_EXE=-o $(TMPDIR)/$(1) diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs index 231ed2898f1..d7ede763838 100644 --- a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs +++ b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs @@ -20,6 +20,7 @@ extern crate rustc_plugin; use std::borrow::ToOwned; use syntax::ast; +use syntax::ext::hygiene; use syntax::ext::build::AstBuilder; use syntax::ext::base::{TTMacroExpander, ExtCtxt, MacResult, MacEager, NormalTT}; use syntax::print::pprust; @@ -54,5 +55,6 @@ pub fn plugin_registrar(reg: &mut Registry) { allow_internal_unstable: false, allow_internal_unsafe: false, unstable_feature: None, + edition: hygiene::default_edition(), }); } diff --git a/src/test/run-pass/auxiliary/edition-kw-macro-2015.rs b/src/test/run-pass/auxiliary/edition-kw-macro-2015.rs new file mode 100644 index 00000000000..9127c8e350a --- /dev/null +++ b/src/test/run-pass/auxiliary/edition-kw-macro-2015.rs @@ -0,0 +1,38 @@ +// Copyright 2018 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: --edition=2015 + +#![feature(raw_identifiers)] + +#[macro_export] +macro_rules! produces_async { + () => (pub fn async() {}) +} + +#[macro_export] +macro_rules! produces_async_raw { + () => (pub fn r#async() {}) +} + +#[macro_export] +macro_rules! consumes_async { + (async) => (1) +} + +#[macro_export] +macro_rules! consumes_async_raw { + (r#async) => (1) +} + +#[macro_export] +macro_rules! passes_ident { + ($i: ident) => ($i) +} diff --git a/src/test/run-pass/auxiliary/edition-kw-macro-2018.rs b/src/test/run-pass/auxiliary/edition-kw-macro-2018.rs new file mode 100644 index 00000000000..4fef77d67ea --- /dev/null +++ b/src/test/run-pass/auxiliary/edition-kw-macro-2018.rs @@ -0,0 +1,38 @@ +// Copyright 2018 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: --edition=2018 + +#![feature(raw_identifiers)] + +#[macro_export] +macro_rules! produces_async { + () => (pub fn async() {}) +} + +#[macro_export] +macro_rules! produces_async_raw { + () => (pub fn r#async() {}) +} + +#[macro_export] +macro_rules! consumes_async { + (async) => (1) +} + +#[macro_export] +macro_rules! consumes_async_raw { + (r#async) => (1) +} + +#[macro_export] +macro_rules! passes_ident { + ($i: ident) => ($i) +} diff --git a/src/test/run-pass/edition-keywords-2015-2015.rs b/src/test/run-pass/edition-keywords-2015-2015.rs new file mode 100644 index 00000000000..41480bb978e --- /dev/null +++ b/src/test/run-pass/edition-keywords-2015-2015.rs @@ -0,0 +1,43 @@ +// Copyright 2018 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: --edition=2015 +// aux-build:edition-kw-macro-2015.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2015; + +pub fn check_async() { + let mut async = 1; // OK + let mut r#async = 1; // OK + + r#async = consumes_async!(async); // OK + // r#async = consumes_async!(r#async); // ERROR, not a match + // r#async = consumes_async_raw!(async); // ERROR, not a match + r#async = consumes_async_raw!(r#async); // OK + + if passes_ident!(async) == 1 {} // OK + if passes_ident!(r#async) == 1 {} // OK + one_async::async(); // OK + one_async::r#async(); // OK + two_async::async(); // OK + two_async::r#async(); // OK +} + +mod one_async { + produces_async! {} // OK +} +mod two_async { + produces_async_raw! {} // OK +} + +fn main() {} diff --git a/src/test/run-pass/edition-keywords-2015-2018.rs b/src/test/run-pass/edition-keywords-2015-2018.rs new file mode 100644 index 00000000000..78835d51063 --- /dev/null +++ b/src/test/run-pass/edition-keywords-2015-2018.rs @@ -0,0 +1,43 @@ +// Copyright 2018 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: --edition=2015 +// aux-build:edition-kw-macro-2018.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2018; + +pub fn check_async() { + let mut async = 1; // OK + let mut r#async = 1; // OK + + r#async = consumes_async!(async); // OK + // r#async = consumes_async!(r#async); // ERROR, not a match + // r#async = consumes_async_raw!(async); // ERROR, not a match + r#async = consumes_async_raw!(r#async); // OK + + if passes_ident!(async) == 1 {} // OK + if passes_ident!(r#async) == 1 {} // OK + // one_async::async(); // ERROR, unresolved name + // one_async::r#async(); // ERROR, unresolved name + two_async::async(); // OK + two_async::r#async(); // OK +} + +mod one_async { + // produces_async! {} // ERROR, reserved +} +mod two_async { + produces_async_raw! {} // OK +} + +fn main() {} diff --git a/src/test/run-pass/edition-keywords-2018-2015.rs b/src/test/run-pass/edition-keywords-2018-2015.rs new file mode 100644 index 00000000000..46d5f222cbb --- /dev/null +++ b/src/test/run-pass/edition-keywords-2018-2015.rs @@ -0,0 +1,43 @@ +// Copyright 2018 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: --edition=2018 +// aux-build:edition-kw-macro-2015.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2015; + +pub fn check_async() { + // let mut async = 1; // ERROR, reserved + let mut r#async = 1; // OK + + r#async = consumes_async!(async); // OK + // r#async = consumes_async!(r#async); // ERROR, not a match + // r#async = consumes_async_raw!(async); // ERROR, not a match + r#async = consumes_async_raw!(r#async); // OK + + // if passes_ident!(async) == 1 {} // ERROR, reserved + if passes_ident!(r#async) == 1 {} // OK + // one_async::async(); // ERROR, reserved + one_async::r#async(); // OK + // two_async::async(); // ERROR, reserved + two_async::r#async(); // OK +} + +mod one_async { + produces_async! {} // OK +} +mod two_async { + produces_async_raw! {} // OK +} + +fn main() {} diff --git a/src/test/run-pass/edition-keywords-2018-2018.rs b/src/test/run-pass/edition-keywords-2018-2018.rs new file mode 100644 index 00000000000..06482988937 --- /dev/null +++ b/src/test/run-pass/edition-keywords-2018-2018.rs @@ -0,0 +1,43 @@ +// Copyright 2018 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: --edition=2018 +// aux-build:edition-kw-macro-2018.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2018; + +pub fn check_async() { + // let mut async = 1; // ERROR, reserved + let mut r#async = 1; // OK + + r#async = consumes_async!(async); // OK + // r#async = consumes_async!(r#async); // ERROR, not a match + // r#async = consumes_async_raw!(async); // ERROR, not a match + r#async = consumes_async_raw!(r#async); // OK + + // if passes_ident!(async) == 1 {} // ERROR, reserved + if passes_ident!(r#async) == 1 {} // OK + // one_async::async(); // ERROR, reserved + // one_async::r#async(); // ERROR, unresolved name + // two_async::async(); // ERROR, reserved + two_async::r#async(); // OK +} + +mod one_async { + // produces_async! {} // ERROR, reserved +} +mod two_async { + produces_async_raw! {} // OK +} + +fn main() {} diff --git a/src/test/run-pass/issue-49955-2.rs b/src/test/run-pass/issue-49955-2.rs new file mode 100644 index 00000000000..17e1de95dd3 --- /dev/null +++ b/src/test/run-pass/issue-49955-2.rs @@ -0,0 +1,26 @@ +// Copyright 2018 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 borrowck=mir + +use std::cell::Cell; + +#[inline(never)] +fn tuple_field() -> &'static u32 { + // This test is MIR-borrowck-only because the old borrowck + // doesn't agree that borrows of "frozen" (i.e. without any + // interior mutability) fields of non-frozen temporaries, + // should be promoted, while MIR promotion does promote them. + &(Cell::new(5), 42).1 +} + +fn main() { + assert_eq!(tuple_field().to_string(), "42"); +} diff --git a/src/test/run-pass/issue-49955.rs b/src/test/run-pass/issue-49955.rs new file mode 100644 index 00000000000..57a1264aaee --- /dev/null +++ b/src/test/run-pass/issue-49955.rs @@ -0,0 +1,30 @@ +// Copyright 2018 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 borrowck=compare + +const ALL_THE_NUMS: [u32; 1] = [ + 1 +]; + +#[inline(never)] +fn array(i: usize) -> &'static u32 { + return &ALL_THE_NUMS[i]; +} + +#[inline(never)] +fn tuple_field() -> &'static u32 { + &(42,).0 +} + +fn main() { + assert_eq!(tuple_field().to_string(), "42"); + assert_eq!(array(0).to_string(), "1"); +} diff --git a/src/test/run-pass/nll/get_default.rs b/src/test/run-pass/nll/get_default.rs deleted file mode 100644 index 13ef907d8d0..00000000000 --- a/src/test/run-pass/nll/get_default.rs +++ /dev/null @@ -1,31 +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. - -#![feature(nll)] - -use std::collections::HashMap; - -fn get_default(map: &mut HashMap<usize, String>, key: usize) -> &mut String { - match map.get_mut(&key) { - Some(value) => value, - None => { - map.insert(key, "".to_string()); - map.get_mut(&key).unwrap() - } - } -} - -fn main() { - let map = &mut HashMap::new(); - map.insert(22, format!("Hello, world")); - map.insert(44, format!("Goodbye, world")); - assert_eq!(&*get_default(map, 22), "Hello, world"); - assert_eq!(&*get_default(map, 66), ""); -} diff --git a/src/test/run-pass/nll/issue-50461-used-mut-from-moves.rs b/src/test/run-pass/nll/issue-50461-used-mut-from-moves.rs new file mode 100644 index 00000000000..d5cf122bf3b --- /dev/null +++ b/src/test/run-pass/nll/issue-50461-used-mut-from-moves.rs @@ -0,0 +1,25 @@ +// 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(nll)] +#![deny(unused_mut)] + +struct Foo { + pub value: i32 +} + +fn use_foo_mut(mut foo: Foo) { + foo = foo; + println!("{}", foo.value); +} + +fn main() { + use_foo_mut(Foo { value: 413 }); +} diff --git a/src/test/rustdoc/auto-impl-primitive.rs b/src/test/rustdoc/auto-impl-primitive.rs new file mode 100644 index 00000000000..a3887b33cc2 --- /dev/null +++ b/src/test/rustdoc/auto-impl-primitive.rs @@ -0,0 +1,17 @@ +// Copyright 2018 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"] +pub use std::fs::File; + +// @has 'foo/primitive.i16.html' '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementation' +#[doc(primitive = "i16")] +/// I love poneys! +mod prim {} diff --git a/src/test/ui/E0508.ast.nll.stderr b/src/test/ui/E0508.ast.nll.stderr new file mode 100644 index 00000000000..28403644a23 --- /dev/null +++ b/src/test/ui/E0508.ast.nll.stderr @@ -0,0 +1,9 @@ +error[E0508]: cannot move out of type `[NonCopy; 1]`, a non-copy array + --> $DIR/E0508.rs:18:18 + | +LL | let _value = array[0]; //[ast]~ ERROR [E0508] + | ^^^^^^^^ cannot move out of here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0508`. diff --git a/src/test/ui/E0508.ast.stderr b/src/test/ui/E0508.ast.stderr new file mode 100644 index 00000000000..5878b795b77 --- /dev/null +++ b/src/test/ui/E0508.ast.stderr @@ -0,0 +1,12 @@ +error[E0508]: cannot move out of type `[NonCopy; 1]`, a non-copy array + --> $DIR/E0508.rs:18:18 + | +LL | let _value = array[0]; //[ast]~ ERROR [E0508] + | ^^^^^^^^ + | | + | cannot move out of here + | help: consider using a reference instead: `&array[0]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0508`. diff --git a/src/test/ui/E0508.mir.stderr b/src/test/ui/E0508.mir.stderr new file mode 100644 index 00000000000..28403644a23 --- /dev/null +++ b/src/test/ui/E0508.mir.stderr @@ -0,0 +1,9 @@ +error[E0508]: cannot move out of type `[NonCopy; 1]`, a non-copy array + --> $DIR/E0508.rs:18:18 + | +LL | let _value = array[0]; //[ast]~ ERROR [E0508] + | ^^^^^^^^ cannot move out of here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0508`. diff --git a/src/test/ui/E0508.rs b/src/test/ui/E0508.rs new file mode 100644 index 00000000000..0c3dce6b034 --- /dev/null +++ b/src/test/ui/E0508.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. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +struct NonCopy; + +fn main() { + let array = [NonCopy; 1]; + let _value = array[0]; //[ast]~ ERROR [E0508] + //[mir]~^ ERROR [E0508] +} diff --git a/src/test/ui/auxiliary/edition-kw-macro-2015.rs b/src/test/ui/auxiliary/edition-kw-macro-2015.rs new file mode 100644 index 00000000000..9127c8e350a --- /dev/null +++ b/src/test/ui/auxiliary/edition-kw-macro-2015.rs @@ -0,0 +1,38 @@ +// Copyright 2018 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: --edition=2015 + +#![feature(raw_identifiers)] + +#[macro_export] +macro_rules! produces_async { + () => (pub fn async() {}) +} + +#[macro_export] +macro_rules! produces_async_raw { + () => (pub fn r#async() {}) +} + +#[macro_export] +macro_rules! consumes_async { + (async) => (1) +} + +#[macro_export] +macro_rules! consumes_async_raw { + (r#async) => (1) +} + +#[macro_export] +macro_rules! passes_ident { + ($i: ident) => ($i) +} diff --git a/src/test/ui/auxiliary/edition-kw-macro-2018.rs b/src/test/ui/auxiliary/edition-kw-macro-2018.rs new file mode 100644 index 00000000000..4fef77d67ea --- /dev/null +++ b/src/test/ui/auxiliary/edition-kw-macro-2018.rs @@ -0,0 +1,38 @@ +// Copyright 2018 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: --edition=2018 + +#![feature(raw_identifiers)] + +#[macro_export] +macro_rules! produces_async { + () => (pub fn async() {}) +} + +#[macro_export] +macro_rules! produces_async_raw { + () => (pub fn r#async() {}) +} + +#[macro_export] +macro_rules! consumes_async { + (async) => (1) +} + +#[macro_export] +macro_rules! consumes_async_raw { + (r#async) => (1) +} + +#[macro_export] +macro_rules! passes_ident { + ($i: ident) => ($i) +} diff --git a/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr b/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr index fc288e6b1d6..2284f0784c5 100644 --- a/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr +++ b/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr @@ -2,19 +2,28 @@ error[E0499]: cannot borrow `*arg` as mutable more than once at a time --> $DIR/mut-borrow-in-loop.rs:20:25 | LL | (self.func)(arg) //~ ERROR cannot borrow - | ^^^ mutable borrow starts here in previous iteration of loop + | ------------^^^- + | | | + | | mutable borrow starts here in previous iteration of loop + | borrow later used here error[E0499]: cannot borrow `*arg` as mutable more than once at a time --> $DIR/mut-borrow-in-loop.rs:26:25 | LL | (self.func)(arg) //~ ERROR cannot borrow - | ^^^ mutable borrow starts here in previous iteration of loop + | ------------^^^- + | | | + | | mutable borrow starts here in previous iteration of loop + | borrow later used here error[E0499]: cannot borrow `*arg` as mutable more than once at a time --> $DIR/mut-borrow-in-loop.rs:33:25 | LL | (self.func)(arg) //~ ERROR cannot borrow - | ^^^ mutable borrow starts here in previous iteration of loop + | ------------^^^- + | | | + | | mutable borrow starts here in previous iteration of loop + | borrow later used here error: aborting due to 3 previous errors diff --git a/src/test/ui/const-eval-overflow-2.rs b/src/test/ui/const-eval-overflow-2.rs index 885edb55ed8..63f33cafaf8 100644 --- a/src/test/ui/const-eval-overflow-2.rs +++ b/src/test/ui/const-eval-overflow-2.rs @@ -11,7 +11,7 @@ // Evaluation of constants in refutable patterns goes through // different compiler control-flow paths. -#![allow(unused_imports, warnings)] +#![allow(unused_imports, warnings, const_err)] use std::fmt; use std::{i8, i16, i32, i64, isize}; diff --git a/src/test/ui/const-eval-overflow-4.rs b/src/test/ui/const-eval-overflow-4.rs index 24e178152ee..ed14036b0b4 100644 --- a/src/test/ui/const-eval-overflow-4.rs +++ b/src/test/ui/const-eval-overflow-4.rs @@ -22,7 +22,7 @@ use std::{u8, u16, u32, u64, usize}; const A_I8_T : [u32; (i8::MAX as i8 + 1i8) as usize] //~^ ERROR E0080 - //~| WARN attempt to add with overflow + //~| ERROR attempt to add with overflow = [0; (i8::MAX as usize) + 1]; fn main() { diff --git a/src/test/ui/const-eval-overflow-4.stderr b/src/test/ui/const-eval-overflow-4.stderr index db0a6fc8204..fc4762f0554 100644 --- a/src/test/ui/const-eval-overflow-4.stderr +++ b/src/test/ui/const-eval-overflow-4.stderr @@ -1,10 +1,10 @@ -warning: attempt to add with overflow +error: attempt to add with overflow --> $DIR/const-eval-overflow-4.rs:23:13 | LL | : [u32; (i8::MAX as i8 + 1i8) as usize] | ^^^^^^^^^^^^^^^^^^^^^ | - = note: #[warn(const_err)] on by default + = note: #[deny(const_err)] on by default error[E0080]: constant evaluation error --> $DIR/const-eval-overflow-4.rs:23:13 @@ -12,6 +12,6 @@ error[E0080]: constant evaluation error LL | : [u32; (i8::MAX as i8 + 1i8) as usize] | ^^^^^^^^^^^^^^^^^^^^^ attempt to add with overflow -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-eval/conditional_array_execution.rs b/src/test/ui/const-eval/conditional_array_execution.rs index dbddee862e0..8952a8386d7 100644 --- a/src/test/ui/const-eval/conditional_array_execution.rs +++ b/src/test/ui/const-eval/conditional_array_execution.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-pass +#![warn(const_err)] const X: u32 = 5; const Y: u32 = 6; diff --git a/src/test/ui/const-eval/conditional_array_execution.stderr b/src/test/ui/const-eval/conditional_array_execution.stderr index 713b1b36c08..5cf73b9fad6 100644 --- a/src/test/ui/const-eval/conditional_array_execution.stderr +++ b/src/test/ui/const-eval/conditional_array_execution.stderr @@ -1,19 +1,23 @@ warning: attempt to subtract with overflow - --> $DIR/conditional_array_execution.rs:15:19 + --> $DIR/conditional_array_execution.rs:16:19 | LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; | ^^^^^ | - = note: #[warn(const_err)] on by default +note: lint level defined here + --> $DIR/conditional_array_execution.rs:12:9 + | +LL | #![warn(const_err)] + | ^^^^^^^^^ warning: this constant cannot be used - --> $DIR/conditional_array_execution.rs:15:1 + --> $DIR/conditional_array_execution.rs:16:1 | LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow warning: constant evaluation error - --> $DIR/conditional_array_execution.rs:20:20 + --> $DIR/conditional_array_execution.rs:21:20 | LL | println!("{}", FOO); | ^^^ referenced constant has errors diff --git a/src/test/ui/const-eval/issue-43197.rs b/src/test/ui/const-eval/issue-43197.rs index 097fba4d3c4..7ec100e411b 100644 --- a/src/test/ui/const-eval/issue-43197.rs +++ b/src/test/ui/const-eval/issue-43197.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-pass +#![warn(const_err)] #![feature(const_fn)] diff --git a/src/test/ui/const-eval/issue-43197.stderr b/src/test/ui/const-eval/issue-43197.stderr index a22e8016296..d0e13d5657e 100644 --- a/src/test/ui/const-eval/issue-43197.stderr +++ b/src/test/ui/const-eval/issue-43197.stderr @@ -1,37 +1,41 @@ warning: attempt to subtract with overflow - --> $DIR/issue-43197.rs:20:20 + --> $DIR/issue-43197.rs:21:20 | LL | const X: u32 = 0-1; | ^^^ | - = note: #[warn(const_err)] on by default +note: lint level defined here + --> $DIR/issue-43197.rs:12:9 + | +LL | #![warn(const_err)] + | ^^^^^^^^^ warning: this constant cannot be used - --> $DIR/issue-43197.rs:20:5 + --> $DIR/issue-43197.rs:21:5 | LL | const X: u32 = 0-1; | ^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow warning: attempt to subtract with overflow - --> $DIR/issue-43197.rs:23:24 + --> $DIR/issue-43197.rs:24:24 | LL | const Y: u32 = foo(0-1); | ^^^ warning: this constant cannot be used - --> $DIR/issue-43197.rs:23:5 + --> $DIR/issue-43197.rs:24:5 | LL | const Y: u32 = foo(0-1); | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow warning: constant evaluation error - --> $DIR/issue-43197.rs:26:23 + --> $DIR/issue-43197.rs:27:23 | LL | println!("{} {}", X, Y); | ^ referenced constant has errors warning: constant evaluation error - --> $DIR/issue-43197.rs:26:26 + --> $DIR/issue-43197.rs:27:26 | LL | println!("{} {}", X, Y); | ^ referenced constant has errors diff --git a/src/test/ui/const-eval/issue-44578.rs b/src/test/ui/const-eval/issue-44578.rs index 765113cfbb9..4133a8864f6 100644 --- a/src/test/ui/const-eval/issue-44578.rs +++ b/src/test/ui/const-eval/issue-44578.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-pass +#![warn(const_err)] trait Foo { const AMT: usize; diff --git a/src/test/ui/const-eval/issue-44578.stderr b/src/test/ui/const-eval/issue-44578.stderr index 01c6fa3623f..ce6ff86610a 100644 --- a/src/test/ui/const-eval/issue-44578.stderr +++ b/src/test/ui/const-eval/issue-44578.stderr @@ -1,13 +1,17 @@ warning: constant evaluation error - --> $DIR/issue-44578.rs:35:20 + --> $DIR/issue-44578.rs:36:20 | LL | println!("{}", <Bar<u16, u8> as Foo>::AMT); //~ WARN const_err | ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors | - = note: #[warn(const_err)] on by default +note: lint level defined here + --> $DIR/issue-44578.rs:12:9 + | +LL | #![warn(const_err)] + | ^^^^^^^^^ warning: constant evaluation error - --> $DIR/issue-44578.rs:35:20 + --> $DIR/issue-44578.rs:36:20 | LL | println!("{}", <Bar<u16, u8> as Foo>::AMT); //~ WARN const_err | ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors diff --git a/src/test/ui/const-eval/issue-50706.rs b/src/test/ui/const-eval/issue-50706.rs new file mode 100644 index 00000000000..2b0082d95b3 --- /dev/null +++ b/src/test/ui/const-eval/issue-50706.rs @@ -0,0 +1,47 @@ +// Copyright 2018 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-pass + +pub struct Stats; + +#[derive(PartialEq, Eq)] +pub struct StatVariant { + pub id: u8, + _priv: (), +} + +#[derive(PartialEq, Eq)] +pub struct Stat { + pub variant: StatVariant, + pub index: usize, + _priv: (), +} + +impl Stats { + pub const TEST: StatVariant = StatVariant{id: 0, _priv: (),}; + #[allow(non_upper_case_globals)] + pub const A: Stat = Stat{ + variant: Self::TEST, + index: 0, + _priv: (),}; +} + +impl Stat { + pub fn from_index(variant: StatVariant, index: usize) -> Option<Stat> { + let stat = Stat{variant, index, _priv: (),}; + match stat { + Stats::A => Some(Stats::A), + _ => None, + } + } +} + +fn main() {} diff --git a/src/test/ui/const-eval/promoted_errors.rs b/src/test/ui/const-eval/promoted_errors.rs index dc30c7f9cce..7385860abae 100644 --- a/src/test/ui/const-eval/promoted_errors.rs +++ b/src/test/ui/const-eval/promoted_errors.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![warn(const_err)] + // compile-pass // compile-flags: -O fn main() { diff --git a/src/test/ui/const-eval/promoted_errors.stderr b/src/test/ui/const-eval/promoted_errors.stderr index 7761f192fdb..8e9a0ea43a4 100644 --- a/src/test/ui/const-eval/promoted_errors.stderr +++ b/src/test/ui/const-eval/promoted_errors.stderr @@ -1,49 +1,53 @@ warning: constant evaluation error - --> $DIR/promoted_errors.rs:14:20 + --> $DIR/promoted_errors.rs:16:20 | LL | println!("{}", 0u32 - 1); | ^^^^^^^^ attempt to subtract with overflow | - = note: #[warn(const_err)] on by default +note: lint level defined here + --> $DIR/promoted_errors.rs:11:9 + | +LL | #![warn(const_err)] + | ^^^^^^^^^ warning: constant evaluation error - --> $DIR/promoted_errors.rs:14:20 + --> $DIR/promoted_errors.rs:16:20 | LL | println!("{}", 0u32 - 1); | ^^^^^^^^ attempt to subtract with overflow warning: constant evaluation error - --> $DIR/promoted_errors.rs:17:14 + --> $DIR/promoted_errors.rs:19:14 | LL | let _x = 0u32 - 1; | ^^^^^^^^ attempt to subtract with overflow warning: attempt to divide by zero - --> $DIR/promoted_errors.rs:19:20 + --> $DIR/promoted_errors.rs:21:20 | LL | println!("{}", 1/(1-1)); | ^^^^^^^ warning: constant evaluation error - --> $DIR/promoted_errors.rs:19:20 + --> $DIR/promoted_errors.rs:21:20 | LL | println!("{}", 1/(1-1)); | ^^^^^^^ attempt to divide by zero warning: attempt to divide by zero - --> $DIR/promoted_errors.rs:22:14 + --> $DIR/promoted_errors.rs:24:14 | LL | let _x = 1/(1-1); | ^^^^^^^ warning: constant evaluation error - --> $DIR/promoted_errors.rs:22:14 + --> $DIR/promoted_errors.rs:24:14 | LL | let _x = 1/(1-1); | ^^^^^^^ attempt to divide by zero warning: constant evaluation error - --> $DIR/promoted_errors.rs:25:20 + --> $DIR/promoted_errors.rs:27:20 | LL | println!("{}", 1/(false as u32)); | ^^^^^^^^^^^^^^^^ attempt to divide by zero diff --git a/src/test/ui/const-eval/pub_const_err.rs b/src/test/ui/const-eval/pub_const_err.rs index c6bf07649af..ef8fdb33d74 100644 --- a/src/test/ui/const-eval/pub_const_err.rs +++ b/src/test/ui/const-eval/pub_const_err.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-pass +#![warn(const_err)] #![crate_type = "lib"] diff --git a/src/test/ui/const-eval/pub_const_err.stderr b/src/test/ui/const-eval/pub_const_err.stderr index 2981ac20cd9..068825f1cd3 100644 --- a/src/test/ui/const-eval/pub_const_err.stderr +++ b/src/test/ui/const-eval/pub_const_err.stderr @@ -1,25 +1,29 @@ warning: attempt to subtract with overflow - --> $DIR/pub_const_err.rs:15:20 + --> $DIR/pub_const_err.rs:16:20 | LL | pub const Z: u32 = 0 - 1; | ^^^^^ | - = note: #[warn(const_err)] on by default +note: lint level defined here + --> $DIR/pub_const_err.rs:12:9 + | +LL | #![warn(const_err)] + | ^^^^^^^^^ warning: this constant cannot be used - --> $DIR/pub_const_err.rs:15:1 + --> $DIR/pub_const_err.rs:16:1 | LL | pub const Z: u32 = 0 - 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow warning: attempt to subtract with overflow - --> $DIR/pub_const_err.rs:19:22 + --> $DIR/pub_const_err.rs:20:22 | LL | pub type Foo = [i32; 0 - 1]; | ^^^^^ warning: this array length cannot be used - --> $DIR/pub_const_err.rs:19:22 + --> $DIR/pub_const_err.rs:20:22 | LL | pub type Foo = [i32; 0 - 1]; | ^^^^^ attempt to subtract with overflow diff --git a/src/test/ui/const-eval/pub_const_err_bin.rs b/src/test/ui/const-eval/pub_const_err_bin.rs index d87cb7ed770..f65da1d8674 100644 --- a/src/test/ui/const-eval/pub_const_err_bin.rs +++ b/src/test/ui/const-eval/pub_const_err_bin.rs @@ -9,6 +9,7 @@ // except according to those terms. // compile-pass +#![warn(const_err)] pub const Z: u32 = 0 - 1; //~^ WARN attempt to subtract with overflow diff --git a/src/test/ui/const-eval/pub_const_err_bin.stderr b/src/test/ui/const-eval/pub_const_err_bin.stderr index 3e8966d854b..dcb8125fc55 100644 --- a/src/test/ui/const-eval/pub_const_err_bin.stderr +++ b/src/test/ui/const-eval/pub_const_err_bin.stderr @@ -1,25 +1,29 @@ warning: attempt to subtract with overflow - --> $DIR/pub_const_err_bin.rs:13:20 + --> $DIR/pub_const_err_bin.rs:14:20 | LL | pub const Z: u32 = 0 - 1; | ^^^^^ | - = note: #[warn(const_err)] on by default +note: lint level defined here + --> $DIR/pub_const_err_bin.rs:12:9 + | +LL | #![warn(const_err)] + | ^^^^^^^^^ warning: this constant cannot be used - --> $DIR/pub_const_err_bin.rs:13:1 + --> $DIR/pub_const_err_bin.rs:14:1 | LL | pub const Z: u32 = 0 - 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow warning: attempt to subtract with overflow - --> $DIR/pub_const_err_bin.rs:17:22 + --> $DIR/pub_const_err_bin.rs:18:22 | LL | pub type Foo = [i32; 0 - 1]; | ^^^^^ warning: this array length cannot be used - --> $DIR/pub_const_err_bin.rs:17:22 + --> $DIR/pub_const_err_bin.rs:18:22 | LL | pub type Foo = [i32; 0 - 1]; | ^^^^^ attempt to subtract with overflow diff --git a/src/test/ui/const-len-underflow-separate-spans.rs b/src/test/ui/const-len-underflow-separate-spans.rs index ee07dabab1f..453e332a903 100644 --- a/src/test/ui/const-len-underflow-separate-spans.rs +++ b/src/test/ui/const-len-underflow-separate-spans.rs @@ -16,7 +16,7 @@ const ONE: usize = 1; const TWO: usize = 2; const LEN: usize = ONE - TWO; //~^ ERROR E0080 -//~| WARN attempt to subtract with overflow +//~| ERROR attempt to subtract with overflow fn main() { let a: [i8; LEN] = unimplemented!(); diff --git a/src/test/ui/const-len-underflow-separate-spans.stderr b/src/test/ui/const-len-underflow-separate-spans.stderr index 8d737dbfc08..48ff7a81c24 100644 --- a/src/test/ui/const-len-underflow-separate-spans.stderr +++ b/src/test/ui/const-len-underflow-separate-spans.stderr @@ -1,10 +1,10 @@ -warning: attempt to subtract with overflow +error: attempt to subtract with overflow --> $DIR/const-len-underflow-separate-spans.rs:17:20 | LL | const LEN: usize = ONE - TWO; | ^^^^^^^^^ | - = note: #[warn(const_err)] on by default + = note: #[deny(const_err)] on by default error[E0080]: constant evaluation error --> $DIR/const-len-underflow-separate-spans.rs:17:20 @@ -18,6 +18,6 @@ error[E0080]: constant evaluation error LL | let a: [i8; LEN] = unimplemented!(); | ^^^ referenced constant has errors -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/edition-keywords-2015-2015-expansion.rs b/src/test/ui/edition-keywords-2015-2015-expansion.rs new file mode 100644 index 00000000000..b8a1994a105 --- /dev/null +++ b/src/test/ui/edition-keywords-2015-2015-expansion.rs @@ -0,0 +1,27 @@ +// Copyright 2018 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: --edition=2015 +// aux-build:edition-kw-macro-2015.rs +// compile-pass + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2015; + +mod one_async { + produces_async! {} // OK +} +mod two_async { + produces_async_raw! {} // OK +} + +fn main() {} diff --git a/src/test/ui/edition-keywords-2015-2015-parsing.rs b/src/test/ui/edition-keywords-2015-2015-parsing.rs new file mode 100644 index 00000000000..1fb91ca006c --- /dev/null +++ b/src/test/ui/edition-keywords-2015-2015-parsing.rs @@ -0,0 +1,32 @@ +// Copyright 2018 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: --edition=2015 +// aux-build:edition-kw-macro-2015.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2015; + +pub fn check_async() { + let mut async = 1; // OK + let mut r#async = 1; // OK + + r#async = consumes_async!(async); // OK + r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async` + r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` + r#async = consumes_async_raw!(r#async); // OK + + if passes_ident!(async) == 1 {} // OK + if passes_ident!(r#async) == 1 {} // OK + module::async(); // OK + module::r#async(); // OK +} diff --git a/src/test/ui/edition-keywords-2015-2015-parsing.stderr b/src/test/ui/edition-keywords-2015-2015-parsing.stderr new file mode 100644 index 00000000000..5b6fd3e1c9c --- /dev/null +++ b/src/test/ui/edition-keywords-2015-2015-parsing.stderr @@ -0,0 +1,14 @@ +error: no rules expected the token `r#async` + --> $DIR/edition-keywords-2015-2015-parsing.rs:24:31 + | +LL | r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async` + | ^^^^^^^ + +error: no rules expected the token `async` + --> $DIR/edition-keywords-2015-2015-parsing.rs:25:35 + | +LL | r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` + | ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/edition-keywords-2015-2018-expansion.rs b/src/test/ui/edition-keywords-2015-2018-expansion.rs new file mode 100644 index 00000000000..bc14c104c49 --- /dev/null +++ b/src/test/ui/edition-keywords-2015-2018-expansion.rs @@ -0,0 +1,24 @@ +// Copyright 2018 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: --edition=2015 +// aux-build:edition-kw-macro-2018.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2018; + +mod one_async { + produces_async! {} // ERROR expected identifier, found reserved keyword +} +mod two_async { + produces_async_raw! {} // OK +} diff --git a/src/test/ui/edition-keywords-2015-2018-expansion.stderr b/src/test/ui/edition-keywords-2015-2018-expansion.stderr new file mode 100644 index 00000000000..13c4ee82537 --- /dev/null +++ b/src/test/ui/edition-keywords-2015-2018-expansion.stderr @@ -0,0 +1,10 @@ +error: expected identifier, found reserved keyword `async` + --> $DIR/edition-keywords-2015-2018-expansion.rs:20:5 + | +LL | produces_async! {} // ERROR expected identifier, found reserved keyword + | ^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/edition-keywords-2015-2018-parsing.rs b/src/test/ui/edition-keywords-2015-2018-parsing.rs new file mode 100644 index 00000000000..0b680eb16c7 --- /dev/null +++ b/src/test/ui/edition-keywords-2015-2018-parsing.rs @@ -0,0 +1,32 @@ +// Copyright 2018 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: --edition=2015 +// aux-build:edition-kw-macro-2018.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2018; + +pub fn check_async() { + let mut async = 1; // OK + let mut r#async = 1; // OK + + r#async = consumes_async!(async); // OK + r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async` + r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` + r#async = consumes_async_raw!(r#async); // OK + + if passes_ident!(async) == 1 {} // OK + if passes_ident!(r#async) == 1 {} // OK + module::async(); // OK + module::r#async(); // OK +} diff --git a/src/test/ui/edition-keywords-2015-2018-parsing.stderr b/src/test/ui/edition-keywords-2015-2018-parsing.stderr new file mode 100644 index 00000000000..60cfbce3ff0 --- /dev/null +++ b/src/test/ui/edition-keywords-2015-2018-parsing.stderr @@ -0,0 +1,14 @@ +error: no rules expected the token `r#async` + --> $DIR/edition-keywords-2015-2018-parsing.rs:24:31 + | +LL | r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async` + | ^^^^^^^ + +error: no rules expected the token `async` + --> $DIR/edition-keywords-2015-2018-parsing.rs:25:35 + | +LL | r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` + | ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/edition-keywords-2018-2015-expansion.rs b/src/test/ui/edition-keywords-2018-2015-expansion.rs new file mode 100644 index 00000000000..6f85f427eb0 --- /dev/null +++ b/src/test/ui/edition-keywords-2018-2015-expansion.rs @@ -0,0 +1,27 @@ +// Copyright 2018 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: --edition=2018 +// aux-build:edition-kw-macro-2015.rs +// compile-pass + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2015; + +mod one_async { + produces_async! {} // OK +} +mod two_async { + produces_async_raw! {} // OK +} + +fn main() {} diff --git a/src/test/ui/edition-keywords-2018-2015-parsing.rs b/src/test/ui/edition-keywords-2018-2015-parsing.rs new file mode 100644 index 00000000000..29c5ea41f1f --- /dev/null +++ b/src/test/ui/edition-keywords-2018-2015-parsing.rs @@ -0,0 +1,32 @@ +// Copyright 2018 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: --edition=2018 +// aux-build:edition-kw-macro-2015.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2015; + +pub fn check_async() { + let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async` + let mut r#async = 1; // OK + + r#async = consumes_async!(async); // OK + r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async` + r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` + r#async = consumes_async_raw!(r#async); // OK + + if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async` + if passes_ident!(r#async) == 1 {} // OK + module::async(); //~ ERROR expected identifier, found reserved keyword `async` + module::r#async(); // OK +} diff --git a/src/test/ui/edition-keywords-2018-2015-parsing.stderr b/src/test/ui/edition-keywords-2018-2015-parsing.stderr new file mode 100644 index 00000000000..0b3ca57bfab --- /dev/null +++ b/src/test/ui/edition-keywords-2018-2015-parsing.stderr @@ -0,0 +1,32 @@ +error: expected identifier, found reserved keyword `async` + --> $DIR/edition-keywords-2018-2015-parsing.rs:20:13 + | +LL | let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async` + | ^^^^^ expected identifier, found reserved keyword + +error: expected identifier, found reserved keyword `async` + --> $DIR/edition-keywords-2018-2015-parsing.rs:30:13 + | +LL | module::async(); //~ ERROR expected identifier, found reserved keyword `async` + | ^^^^^ expected identifier, found reserved keyword + +error: no rules expected the token `r#async` + --> $DIR/edition-keywords-2018-2015-parsing.rs:24:31 + | +LL | r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async` + | ^^^^^^^ + +error: no rules expected the token `async` + --> $DIR/edition-keywords-2018-2015-parsing.rs:25:35 + | +LL | r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` + | ^^^^^ + +error: expected expression, found reserved keyword `async` + --> $DIR/edition-keywords-2018-2015-parsing.rs:28:22 + | +LL | if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async` + | ^^^^^ expected expression + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/edition-keywords-2018-2018-expansion.rs b/src/test/ui/edition-keywords-2018-2018-expansion.rs new file mode 100644 index 00000000000..ef7f63e225c --- /dev/null +++ b/src/test/ui/edition-keywords-2018-2018-expansion.rs @@ -0,0 +1,24 @@ +// Copyright 2018 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: --edition=2018 +// aux-build:edition-kw-macro-2018.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2018; + +mod one_async { + produces_async! {} // ERROR expected identifier, found reserved keyword `async` +} +mod two_async { + produces_async_raw! {} // OK +} diff --git a/src/test/ui/edition-keywords-2018-2018-expansion.stderr b/src/test/ui/edition-keywords-2018-2018-expansion.stderr new file mode 100644 index 00000000000..cd51030fd28 --- /dev/null +++ b/src/test/ui/edition-keywords-2018-2018-expansion.stderr @@ -0,0 +1,10 @@ +error: expected identifier, found reserved keyword `async` + --> $DIR/edition-keywords-2018-2018-expansion.rs:20:5 + | +LL | produces_async! {} // ERROR expected identifier, found reserved keyword `async` + | ^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/edition-keywords-2018-2018-parsing.rs b/src/test/ui/edition-keywords-2018-2018-parsing.rs new file mode 100644 index 00000000000..a94808eb224 --- /dev/null +++ b/src/test/ui/edition-keywords-2018-2018-parsing.rs @@ -0,0 +1,32 @@ +// Copyright 2018 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: --edition=2018 +// aux-build:edition-kw-macro-2018.rs + +#![feature(raw_identifiers)] + +#[macro_use] +extern crate edition_kw_macro_2018; + +pub fn check_async() { + let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async` + let mut r#async = 1; // OK + + r#async = consumes_async!(async); // OK + r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async` + r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` + r#async = consumes_async_raw!(r#async); // OK + + if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async` + if passes_ident!(r#async) == 1 {} // OK + module::async(); //~ ERROR expected identifier, found reserved keyword `async` + module::r#async(); // OK +} diff --git a/src/test/ui/edition-keywords-2018-2018-parsing.stderr b/src/test/ui/edition-keywords-2018-2018-parsing.stderr new file mode 100644 index 00000000000..1b18d8a39be --- /dev/null +++ b/src/test/ui/edition-keywords-2018-2018-parsing.stderr @@ -0,0 +1,32 @@ +error: expected identifier, found reserved keyword `async` + --> $DIR/edition-keywords-2018-2018-parsing.rs:20:13 + | +LL | let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async` + | ^^^^^ expected identifier, found reserved keyword + +error: expected identifier, found reserved keyword `async` + --> $DIR/edition-keywords-2018-2018-parsing.rs:30:13 + | +LL | module::async(); //~ ERROR expected identifier, found reserved keyword `async` + | ^^^^^ expected identifier, found reserved keyword + +error: no rules expected the token `r#async` + --> $DIR/edition-keywords-2018-2018-parsing.rs:24:31 + | +LL | r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async` + | ^^^^^^^ + +error: no rules expected the token `async` + --> $DIR/edition-keywords-2018-2018-parsing.rs:25:35 + | +LL | r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async` + | ^^^^^ + +error: expected expression, found reserved keyword `async` + --> $DIR/edition-keywords-2018-2018-parsing.rs:28:22 + | +LL | if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async` + | ^^^^^ expected expression + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/error-codes/E0080.stderr b/src/test/ui/error-codes/E0080.stderr index 5e401bd6c79..25ec5c45831 100644 --- a/src/test/ui/error-codes/E0080.stderr +++ b/src/test/ui/error-codes/E0080.stderr @@ -12,15 +12,15 @@ error[E0080]: constant evaluation error LL | X = (1 << 500), //~ ERROR E0080 | ^^^^^^^^^^ attempt to shift left with overflow -warning: attempt to divide by zero +error: attempt to divide by zero --> $DIR/E0080.rs:14:9 | LL | Y = (1 / 0) //~ ERROR E0080 | ^^^^^^^ | - = note: #[warn(const_err)] on by default + = note: #[deny(const_err)] on by default -warning: constant evaluation error +error: constant evaluation error --> $DIR/E0080.rs:14:9 | LL | Y = (1 / 0) //~ ERROR E0080 @@ -32,6 +32,6 @@ error[E0080]: constant evaluation error LL | Y = (1 / 0) //~ ERROR E0080 | ^^^^^^^ attempt to divide by zero -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/run-pass/align-offset-sign.rs b/src/test/ui/error-codes/E0646.rs index aaa0419d061..5fc711d9408 100644 --- a/src/test/run-pass/align-offset-sign.rs +++ b/src/test/ui/error-codes/E0646.rs @@ -1,4 +1,4 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,9 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(align_offset)] - -fn main() { - let x = 1 as *const u8; - assert_eq!(x.align_offset(8), 7); -} +fn main() where (): Copy {} //~ ERROR [E0646] diff --git a/src/test/ui/error-codes/E0646.stderr b/src/test/ui/error-codes/E0646.stderr new file mode 100644 index 00000000000..96da22a643c --- /dev/null +++ b/src/test/ui/error-codes/E0646.stderr @@ -0,0 +1,9 @@ +error[E0646]: main function is not allowed to have a where clause + --> $DIR/E0646.rs:11:1 + | +LL | fn main() where (): Copy {} //~ ERROR [E0646] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0646`. diff --git a/src/test/run-make-fulldeps/issue-36710/foo.cpp b/src/test/ui/error-codes/E0647.rs index fbd0ead7a50..0a0ffefdf95 100644 --- a/src/test/run-make-fulldeps/issue-36710/foo.cpp +++ b/src/test/ui/error-codes/E0647.rs @@ -8,18 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#include <stdint.h> +#![no_std] +#![feature(start)] -struct A { - A() { v = 1234; } - ~A() { v = 1; } - uint32_t v; -}; +extern crate std; -A a; - -extern "C" { - uint32_t get() { - return a.v; - } +#[start] +fn start(_: isize, _: *const *const u8) -> isize where (): Copy { //~ ERROR [E0647] + 0 } diff --git a/src/test/ui/error-codes/E0647.stderr b/src/test/ui/error-codes/E0647.stderr new file mode 100644 index 00000000000..f02407dcea6 --- /dev/null +++ b/src/test/ui/error-codes/E0647.stderr @@ -0,0 +1,11 @@ +error[E0647]: start function is not allowed to have a where clause + --> $DIR/E0647.rs:17:1 + | +LL | / fn start(_: isize, _: *const *const u8) -> isize where (): Copy { //~ ERROR [E0647] +LL | | 0 +LL | | } + | |_^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0647`. diff --git a/src/test/ui/fmt/format-string-error.rs b/src/test/ui/fmt/format-string-error.rs index ec715b3f0ba..5b13686240e 100644 --- a/src/test/ui/fmt/format-string-error.rs +++ b/src/test/ui/fmt/format-string-error.rs @@ -12,5 +12,14 @@ fn main() { println!("{"); println!("{{}}"); println!("}"); + let _ = format!("{_foo}", _foo = 6usize); + //~^ ERROR invalid format string: invalid argument name `_foo` + let _ = format!("{_}", _ = 6usize); + //~^ ERROR invalid format string: invalid argument name `_` + let _ = format!("{"); + //~^ ERROR invalid format string: expected `'}'` but string was terminated + let _ = format!("}"); + //~^ ERROR invalid format string: unmatched `}` found + let _ = format!("{\\}"); + //~^ ERROR invalid format string: expected `'}'`, found `'\\'` } - diff --git a/src/test/ui/fmt/format-string-error.stderr b/src/test/ui/fmt/format-string-error.stderr index a7a66722e52..ff766ddc8fa 100644 --- a/src/test/ui/fmt/format-string-error.stderr +++ b/src/test/ui/fmt/format-string-error.stderr @@ -2,7 +2,7 @@ error: invalid format string: expected `'}'` but string was terminated --> $DIR/format-string-error.rs:12:5 | LL | println!("{"); - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) @@ -11,10 +11,48 @@ error: invalid format string: unmatched `}` found --> $DIR/format-string-error.rs:14:5 | LL | println!("}"); - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ unmatched `}` in format string | = note: if you intended to print `}`, you can escape it using `}}` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) -error: aborting due to 2 previous errors +error: invalid format string: invalid argument name `_foo` + --> $DIR/format-string-error.rs:15:23 + | +LL | let _ = format!("{_foo}", _foo = 6usize); + | ^^^^ invalid argument name in format string + | + = note: argument names cannot start with an underscore + +error: invalid format string: invalid argument name `_` + --> $DIR/format-string-error.rs:17:23 + | +LL | let _ = format!("{_}", _ = 6usize); + | ^ invalid argument name in format string + | + = note: argument names cannot start with an underscore + +error: invalid format string: expected `'}'` but string was terminated + --> $DIR/format-string-error.rs:19:23 + | +LL | let _ = format!("{"); + | ^ expected `'}'` in format string + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: unmatched `}` found + --> $DIR/format-string-error.rs:21:22 + | +LL | let _ = format!("}"); + | ^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + +error: invalid format string: expected `'}'`, found `'/'` + --> $DIR/format-string-error.rs:23:23 + | +LL | let _ = format!("{/}"); + | ^ expected `}` in format string + +error: aborting due to 7 previous errors diff --git a/src/test/ui/imports/rfc-1560-warning-cycle.rs b/src/test/ui/imports/rfc-1560-warning-cycle.rs index f94fc3633e3..5b62c5fcd3f 100644 --- a/src/test/ui/imports/rfc-1560-warning-cycle.rs +++ b/src/test/ui/imports/rfc-1560-warning-cycle.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(unused)] - pub struct Foo; mod bar { @@ -18,9 +16,7 @@ mod bar { mod baz { use *; use bar::*; - fn f(_: Foo) {} - //~^ ERROR `Foo` is ambiguous - //~| WARN hard error in a future release + fn f(_: Foo) {} //~ ERROR `Foo` is ambiguous } } diff --git a/src/test/ui/imports/rfc-1560-warning-cycle.stderr b/src/test/ui/imports/rfc-1560-warning-cycle.stderr index 452fcc4c1a9..5a01680fc19 100644 --- a/src/test/ui/imports/rfc-1560-warning-cycle.stderr +++ b/src/test/ui/imports/rfc-1560-warning-cycle.stderr @@ -1,16 +1,21 @@ -error: `Foo` is ambiguous - --> $DIR/rfc-1560-warning-cycle.rs:21:17 +error[E0659]: `Foo` is ambiguous + --> $DIR/rfc-1560-warning-cycle.rs:19:17 | -LL | use *; - | - `Foo` could refer to the name imported here -LL | use bar::*; - | ------ `Foo` could also refer to the name imported here -LL | fn f(_: Foo) {} +LL | fn f(_: Foo) {} //~ ERROR `Foo` is ambiguous | ^^^ | - = note: #[deny(legacy_imports)] on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #38260 <https://github.com/rust-lang/rust/issues/38260> +note: `Foo` could refer to the name imported here + --> $DIR/rfc-1560-warning-cycle.rs:17:13 + | +LL | use *; + | ^ +note: `Foo` could also refer to the name imported here + --> $DIR/rfc-1560-warning-cycle.rs:18:13 + | +LL | use bar::*; + | ^^^^^^ + = note: consider adding an explicit import of `Foo` to disambiguate error: aborting due to previous error +For more information about this error, try `rustc --explain E0659`. diff --git a/src/test/ui/issue-50714-1.rs b/src/test/ui/issue-50714-1.rs new file mode 100644 index 00000000000..f0e496a88fb --- /dev/null +++ b/src/test/ui/issue-50714-1.rs @@ -0,0 +1,22 @@ +// Copyright 2018 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. + +// Regression test for issue 50714, make sure that this isn't a linker error. + +#![no_std] +#![feature(start)] + +extern crate std; + +#[start] +fn start(_: isize, _: *const *const u8) -> isize where fn(&()): Eq { //~ ERROR [E0647] + 0 +} + diff --git a/src/test/ui/issue-50714-1.stderr b/src/test/ui/issue-50714-1.stderr new file mode 100644 index 00000000000..b93183d2f24 --- /dev/null +++ b/src/test/ui/issue-50714-1.stderr @@ -0,0 +1,11 @@ +error[E0647]: start function is not allowed to have a where clause + --> $DIR/issue-50714-1.rs:19:1 + | +LL | / fn start(_: isize, _: *const *const u8) -> isize where fn(&()): Eq { //~ ERROR [E0647] +LL | | 0 +LL | | } + | |_^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0647`. diff --git a/src/test/ui/issue-50714.rs b/src/test/ui/issue-50714.rs new file mode 100644 index 00000000000..08d975326df --- /dev/null +++ b/src/test/ui/issue-50714.rs @@ -0,0 +1,14 @@ +// Copyright 2018 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. + +// Regression test for issue 50714, make sure that this isn't a linker error. + +fn main() where fn(&()): Eq {} //~ ERROR [E0646] + diff --git a/src/test/ui/issue-50714.stderr b/src/test/ui/issue-50714.stderr new file mode 100644 index 00000000000..8cdcfe4abe2 --- /dev/null +++ b/src/test/ui/issue-50714.stderr @@ -0,0 +1,9 @@ +error[E0646]: main function is not allowed to have a where clause + --> $DIR/issue-50714.rs:13:1 + | +LL | fn main() where fn(&()): Eq {} //~ ERROR [E0646] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0646`. diff --git a/src/test/ui/issue-50761.rs b/src/test/ui/issue-50761.rs new file mode 100644 index 00000000000..b8a7a089c23 --- /dev/null +++ b/src/test/ui/issue-50761.rs @@ -0,0 +1,33 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <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. + +// Confirm that we don't accidently divide or mod by zero in llvm_type + +// compile-pass + +mod a { + pub trait A {} +} + +mod b { + pub struct Builder {} + + pub fn new() -> Builder { + Builder {} + } + + impl Builder { + pub fn with_a(&mut self, _a: fn() -> ::a::A) {} + } +} + +pub use self::b::new; + +fn main() {} diff --git a/src/test/run-make-fulldeps/issue-36710/foo.rs b/src/test/ui/issue-50802.rs index 6e50566ddfd..6342d0757ee 100644 --- a/src/test/run-make-fulldeps/issue-36710/foo.rs +++ b/src/test/ui/issue-50802.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Tests that linking to C++ code with global destructors works. - -extern { fn get() -> u32; } +#[allow(unreachable_code)] fn main() { - let i = unsafe { get() }; - assert_eq!(i, 1234); + loop { + break while continue { //~ ERROR E0590 + } + } } diff --git a/src/test/ui/issue-50802.stderr b/src/test/ui/issue-50802.stderr new file mode 100644 index 00000000000..9da2648b376 --- /dev/null +++ b/src/test/ui/issue-50802.stderr @@ -0,0 +1,9 @@ +error[E0590]: `break` or `continue` with no label in the condition of a `while` loop + --> $DIR/issue-50802.rs:15:21 + | +LL | break while continue { //~ ERROR E0590 + | ^^^^^^^^ unlabeled `continue` in the condition of a `while` loop + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0590`. diff --git a/src/test/ui/label_break_value_continue.rs b/src/test/ui/label_break_value_continue.rs index 52e24b759d1..4a505dff3d4 100644 --- a/src/test/ui/label_break_value_continue.rs +++ b/src/test/ui/label_break_value_continue.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(label_break_value)] +#![allow(unused_labels)] // Simple continue pointing to an unlabeled break should yield in an error fn continue_simple() { diff --git a/src/test/ui/label_break_value_continue.stderr b/src/test/ui/label_break_value_continue.stderr index 24c2d1a22d0..12a21a8a594 100644 --- a/src/test/ui/label_break_value_continue.stderr +++ b/src/test/ui/label_break_value_continue.stderr @@ -1,17 +1,17 @@ error[E0695]: unlabeled `continue` inside of a labeled block - --> $DIR/label_break_value_continue.rs:16:9 + --> $DIR/label_break_value_continue.rs:17:9 | LL | continue; //~ ERROR unlabeled `continue` inside of a labeled block | ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label error[E0696]: `continue` pointing to a labeled block - --> $DIR/label_break_value_continue.rs:23:9 + --> $DIR/label_break_value_continue.rs:24:9 | LL | continue 'b; //~ ERROR `continue` pointing to a labeled block | ^^^^^^^^^^^ labeled blocks cannot be `continue`'d | note: labeled block the continue points to - --> $DIR/label_break_value_continue.rs:22:5 + --> $DIR/label_break_value_continue.rs:23:5 | LL | / 'b: { LL | | continue 'b; //~ ERROR `continue` pointing to a labeled block @@ -19,7 +19,7 @@ LL | | } | |_____^ error[E0695]: unlabeled `continue` inside of a labeled block - --> $DIR/label_break_value_continue.rs:31:13 + --> $DIR/label_break_value_continue.rs:32:13 | LL | continue; //~ ERROR unlabeled `continue` inside of a labeled block | ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label diff --git a/src/test/ui/label_break_value_unlabeled_break.rs b/src/test/ui/label_break_value_unlabeled_break.rs index 38918da291c..454ebd4c6cf 100644 --- a/src/test/ui/label_break_value_unlabeled_break.rs +++ b/src/test/ui/label_break_value_unlabeled_break.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(label_break_value)] +#![allow(unused_labels)] // Simple unlabeled break should yield in an error fn unlabeled_break_simple() { diff --git a/src/test/ui/label_break_value_unlabeled_break.stderr b/src/test/ui/label_break_value_unlabeled_break.stderr index 8a25975a7bd..62c4a12231b 100644 --- a/src/test/ui/label_break_value_unlabeled_break.stderr +++ b/src/test/ui/label_break_value_unlabeled_break.stderr @@ -1,11 +1,11 @@ error[E0695]: unlabeled `break` inside of a labeled block - --> $DIR/label_break_value_unlabeled_break.rs:16:9 + --> $DIR/label_break_value_unlabeled_break.rs:17:9 | LL | break; //~ ERROR unlabeled `break` inside of a labeled block | ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label error[E0695]: unlabeled `break` inside of a labeled block - --> $DIR/label_break_value_unlabeled_break.rs:24:13 + --> $DIR/label_break_value_unlabeled_break.rs:25:13 | LL | break; //~ ERROR unlabeled `break` inside of a labeled block | ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label diff --git a/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.rs b/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.rs index 100fb6d3533..bac3f00ffc7 100644 --- a/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.rs +++ b/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.rs @@ -20,6 +20,11 @@ struct SoulHistory { endless_and_singing: bool } +struct LovelyAmbition { + lips: usize, + fire: usize +} + #[derive(Clone, Copy)] enum Large { Suit { case: () } @@ -45,6 +50,10 @@ fn main() { hours_are_suns = false; } + let the_spirit = LovelyAmbition { lips: 1, fire: 2 }; + let LovelyAmbition { lips, fire } = the_spirit; + println!("{}", lips); + let bag = Large::Suit { case: () }; diff --git a/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.stderr b/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.stderr index 992be2c0a28..a8b0e3e4250 100644 --- a/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.stderr +++ b/src/test/ui/lint/issue-47390-unused-variable-in-struct-pattern.stderr @@ -1,5 +1,5 @@ warning: unused variable: `i_think_continually` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:31:9 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:36:9 | LL | let i_think_continually = 2; | ^^^^^^^^^^^^^^^^^^^ help: consider using `_i_think_continually` instead @@ -12,31 +12,31 @@ LL | #![warn(unused)] // UI tests pass `-A unused` (#43896) = note: #[warn(unused_variables)] implied by #[warn(unused)] warning: unused variable: `mut_unused_var` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:38:13 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:43:13 | LL | let mut mut_unused_var = 1; | ^^^^^^^^^^^^^^ help: consider using `_mut_unused_var` instead warning: unused variable: `var` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:40:14 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:45:14 | LL | let (mut var, unused_var) = (1, 2); | ^^^ help: consider using `_var` instead warning: unused variable: `unused_var` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:40:19 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:45:19 | LL | let (mut var, unused_var) = (1, 2); | ^^^^^^^^^^ help: consider using `_unused_var` instead warning: unused variable: `corridors_of_light` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:42:26 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:47:26 | LL | if let SoulHistory { corridors_of_light, | ^^^^^^^^^^^^^^^^^^ help: try ignoring the field: `corridors_of_light: _` warning: variable `hours_are_suns` is assigned to, but never used - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:43:30 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:48:30 | LL | mut hours_are_suns, | ^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | mut hours_are_suns, = note: consider using `_hours_are_suns` instead warning: value assigned to `hours_are_suns` is never read - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:45:9 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:50:9 | LL | hours_are_suns = false; | ^^^^^^^^^^^^^^ @@ -56,44 +56,50 @@ LL | #![warn(unused)] // UI tests pass `-A unused` (#43896) | ^^^^^^ = note: #[warn(unused_assignments)] implied by #[warn(unused)] +warning: unused variable: `fire` + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:54:32 + | +LL | let LovelyAmbition { lips, fire } = the_spirit; + | ^^^^ help: try ignoring the field: `fire: _` + warning: unused variable: `case` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:54:23 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:63:23 | LL | Large::Suit { case } => {} | ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:59:24 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:68:24 | LL | &Large::Suit { case } => {} | ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:64:27 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:73:27 | LL | box Large::Suit { case } => {} | ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:69:24 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:78:24 | LL | (Large::Suit { case },) => {} | ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:74:24 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:83:24 | LL | [Large::Suit { case }] => {} | ^^^^ help: try ignoring the field: `case: _` warning: unused variable: `case` - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:79:29 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:88:29 | LL | Tuple(Large::Suit { case }, ()) => {} | ^^^^ help: try ignoring the field: `case: _` warning: variable does not need to be mutable - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:38:9 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:43:9 | LL | let mut mut_unused_var = 1; | ----^^^^^^^^^^^^^^ @@ -108,7 +114,7 @@ LL | #![warn(unused)] // UI tests pass `-A unused` (#43896) = note: #[warn(unused_mut)] implied by #[warn(unused)] warning: variable does not need to be mutable - --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:40:10 + --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:45:10 | LL | let (mut var, unused_var) = (1, 2); | ----^^^ diff --git a/src/test/ui/lint/unused_labels.rs b/src/test/ui/lint/unused_labels.rs new file mode 100644 index 00000000000..23add604da6 --- /dev/null +++ b/src/test/ui/lint/unused_labels.rs @@ -0,0 +1,96 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <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 output should warn when a loop label is not used. However, it +// should also deal with the edge cases where a label is shadowed, +// within nested loops + +// compile-pass + +#![feature(label_break_value)] +#![warn(unused_labels)] + +fn main() { + 'unused_while_label: while 0 == 0 { + //~^ WARN unused label + } + + let opt = Some(0); + 'unused_while_let_label: while let Some(_) = opt { + //~^ WARN unused label + } + + 'unused_for_label: for _ in 0..10 { + //~^ WARN unused label + } + + 'used_loop_label: loop { + break 'used_loop_label; + } + + 'used_loop_label_outer_1: for _ in 0..10 { + 'used_loop_label_inner_1: for _ in 0..10 { + break 'used_loop_label_inner_1; + } + break 'used_loop_label_outer_1; + } + + 'used_loop_label_outer_2: for _ in 0..10 { + 'unused_loop_label_inner_2: for _ in 0..10 { + //~^ WARN unused label + break 'used_loop_label_outer_2; + } + } + + 'unused_loop_label_outer_3: for _ in 0..10 { + //~^ WARN unused label + 'used_loop_label_inner_3: for _ in 0..10 { + break 'used_loop_label_inner_3; + } + } + + // You should be able to break the same label many times + 'many_used: loop { + if true { + break 'many_used; + } else { + break 'many_used; + } + } + + // Test breaking many times with the same inner label doesn't break the + // warning on the outer label + 'many_used_shadowed: for _ in 0..10 { + //~^ WARN unused label + 'many_used_shadowed: for _ in 0..10 { + //~^ WARN label name `'many_used_shadowed` shadows a label name that is already in scope + if 1 % 2 == 0 { + break 'many_used_shadowed; + } else { + break 'many_used_shadowed; + } + } + } + + 'unused_loop_label: loop { + //~^ WARN unused label + break; + } + + // Make sure unused block labels give warnings... + 'unused_block_label: { + //~^ WARN unused label + } + + // ...and that used ones don't: + 'used_block_label: { + break 'used_block_label; + } +} diff --git a/src/test/ui/lint/unused_labels.stderr b/src/test/ui/lint/unused_labels.stderr new file mode 100644 index 00000000000..825f5e281f0 --- /dev/null +++ b/src/test/ui/lint/unused_labels.stderr @@ -0,0 +1,63 @@ +warning: unused label + --> $DIR/unused_labels.rs:21:5 + | +LL | 'unused_while_label: while 0 == 0 { + | ^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/unused_labels.rs:18:9 + | +LL | #![warn(unused_labels)] + | ^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:26:5 + | +LL | 'unused_while_let_label: while let Some(_) = opt { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:30:5 + | +LL | 'unused_for_label: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:46:9 + | +LL | 'unused_loop_label_inner_2: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:52:5 + | +LL | 'unused_loop_label_outer_3: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:70:5 + | +LL | 'many_used_shadowed: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:82:5 + | +LL | 'unused_loop_label: loop { + | ^^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:88:5 + | +LL | 'unused_block_label: { + | ^^^^^^^^^^^^^^^^^^^ + +warning: label name `'many_used_shadowed` shadows a label name that is already in scope + --> $DIR/unused_labels.rs:72:9 + | +LL | 'many_used_shadowed: for _ in 0..10 { + | ------------------- first declared here +LL | //~^ WARN unused label +LL | 'many_used_shadowed: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^^^ lifetime 'many_used_shadowed already in scope + diff --git a/src/test/ui/loops-reject-duplicate-labels-2.rs b/src/test/ui/loops-reject-duplicate-labels-2.rs index 598c7370b89..b273e7a0c7c 100644 --- a/src/test/ui/loops-reject-duplicate-labels-2.rs +++ b/src/test/ui/loops-reject-duplicate-labels-2.rs @@ -18,6 +18,7 @@ // discussed here: // https://internals.rust-lang.org/t/psa-rejecting-duplicate-loop-labels/1833 +#[allow(unused_labels)] pub fn foo() { { 'fl: for _ in 0..10 { break; } } { 'fl: loop { break; } } //~ WARN label name `'fl` shadows a label name that is already in scope diff --git a/src/test/ui/loops-reject-duplicate-labels-2.stderr b/src/test/ui/loops-reject-duplicate-labels-2.stderr index 830270a99d1..41b4a850f1b 100644 --- a/src/test/ui/loops-reject-duplicate-labels-2.stderr +++ b/src/test/ui/loops-reject-duplicate-labels-2.stderr @@ -1,5 +1,5 @@ warning: label name `'fl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:23:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:24:7 | LL | { 'fl: for _ in 0..10 { break; } } | --- first declared here @@ -7,7 +7,7 @@ LL | { 'fl: loop { break; } } //~ WARN label name `'fl` shadows | ^^^ lifetime 'fl already in scope warning: label name `'lf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:25:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:26:7 | LL | { 'lf: loop { break; } } | --- first declared here @@ -15,7 +15,7 @@ LL | { 'lf: for _ in 0..10 { break; } } //~ WARN label name `'lf` shadows | ^^^ lifetime 'lf already in scope warning: label name `'wl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:27:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:28:7 | LL | { 'wl: while 2 > 1 { break; } } | --- first declared here @@ -23,7 +23,7 @@ LL | { 'wl: loop { break; } } //~ WARN label name `'wl` shadows | ^^^ lifetime 'wl already in scope warning: label name `'lw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:29:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:30:7 | LL | { 'lw: loop { break; } } | --- first declared here @@ -31,7 +31,7 @@ LL | { 'lw: while 2 > 1 { break; } } //~ WARN label name `'lw` shadows | ^^^ lifetime 'lw already in scope warning: label name `'fw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:31:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:32:7 | LL | { 'fw: for _ in 0..10 { break; } } | --- first declared here @@ -39,7 +39,7 @@ LL | { 'fw: while 2 > 1 { break; } } //~ WARN label name `'fw` shadows | ^^^ lifetime 'fw already in scope warning: label name `'wf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:33:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:34:7 | LL | { 'wf: while 2 > 1 { break; } } | --- first declared here @@ -47,7 +47,7 @@ LL | { 'wf: for _ in 0..10 { break; } } //~ WARN label name `'wf` shadows | ^^^ lifetime 'wf already in scope warning: label name `'tl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:35:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:36:7 | LL | { 'tl: while let Some(_) = None::<i32> { break; } } | --- first declared here @@ -55,7 +55,7 @@ LL | { 'tl: loop { break; } } //~ WARN label name `'tl` shadows | ^^^ lifetime 'tl already in scope warning: label name `'lt` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:37:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:38:7 | LL | { 'lt: loop { break; } } | --- first declared here @@ -63,7 +63,7 @@ LL | { 'lt: while let Some(_) = None::<i32> { break; } } | ^^^ lifetime 'lt already in scope error: compilation successful - --> $DIR/loops-reject-duplicate-labels-2.rs:42:1 + --> $DIR/loops-reject-duplicate-labels-2.rs:43:1 | LL | / pub fn main() { //~ ERROR compilation successful LL | | foo(); diff --git a/src/test/ui/loops-reject-duplicate-labels.rs b/src/test/ui/loops-reject-duplicate-labels.rs index d768b002ab1..ad24f69871c 100644 --- a/src/test/ui/loops-reject-duplicate-labels.rs +++ b/src/test/ui/loops-reject-duplicate-labels.rs @@ -15,6 +15,7 @@ // Issue #21633: reject duplicate loop labels in function bodies. // This is testing the exact cases that are in the issue description. +#[allow(unused_labels)] fn foo() { 'fl: for _ in 0..10 { break; } 'fl: loop { break; } //~ WARN label name `'fl` shadows a label name that is already in scope diff --git a/src/test/ui/loops-reject-duplicate-labels.stderr b/src/test/ui/loops-reject-duplicate-labels.stderr index a71f98b812a..d0cb81544f8 100644 --- a/src/test/ui/loops-reject-duplicate-labels.stderr +++ b/src/test/ui/loops-reject-duplicate-labels.stderr @@ -1,5 +1,5 @@ warning: label name `'fl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:20:5 + --> $DIR/loops-reject-duplicate-labels.rs:21:5 | LL | 'fl: for _ in 0..10 { break; } | --- first declared here @@ -7,7 +7,7 @@ LL | 'fl: loop { break; } //~ WARN label name `'fl` shadows a labe | ^^^ lifetime 'fl already in scope warning: label name `'lf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:23:5 + --> $DIR/loops-reject-duplicate-labels.rs:24:5 | LL | 'lf: loop { break; } | --- first declared here @@ -15,7 +15,7 @@ LL | 'lf: for _ in 0..10 { break; } //~ WARN label name `'lf` shadows a labe | ^^^ lifetime 'lf already in scope warning: label name `'wl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:25:5 + --> $DIR/loops-reject-duplicate-labels.rs:26:5 | LL | 'wl: while 2 > 1 { break; } | --- first declared here @@ -23,7 +23,7 @@ LL | 'wl: loop { break; } //~ WARN label name `'wl` shadows a labe | ^^^ lifetime 'wl already in scope warning: label name `'lw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:27:5 + --> $DIR/loops-reject-duplicate-labels.rs:28:5 | LL | 'lw: loop { break; } | --- first declared here @@ -31,7 +31,7 @@ LL | 'lw: while 2 > 1 { break; } //~ WARN label name `'lw` shadows a labe | ^^^ lifetime 'lw already in scope warning: label name `'fw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:29:5 + --> $DIR/loops-reject-duplicate-labels.rs:30:5 | LL | 'fw: for _ in 0..10 { break; } | --- first declared here @@ -39,7 +39,7 @@ LL | 'fw: while 2 > 1 { break; } //~ WARN label name `'fw` shadows a labe | ^^^ lifetime 'fw already in scope warning: label name `'wf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:31:5 + --> $DIR/loops-reject-duplicate-labels.rs:32:5 | LL | 'wf: while 2 > 1 { break; } | --- first declared here @@ -47,7 +47,7 @@ LL | 'wf: for _ in 0..10 { break; } //~ WARN label name `'wf` shadows a labe | ^^^ lifetime 'wf already in scope warning: label name `'tl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:33:5 + --> $DIR/loops-reject-duplicate-labels.rs:34:5 | LL | 'tl: while let Some(_) = None::<i32> { break; } | --- first declared here @@ -55,7 +55,7 @@ LL | 'tl: loop { break; } //~ WARN label name `'tl` shadows a labe | ^^^ lifetime 'tl already in scope warning: label name `'lt` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:35:5 + --> $DIR/loops-reject-duplicate-labels.rs:36:5 | LL | 'lt: loop { break; } | --- first declared here @@ -63,7 +63,7 @@ LL | 'lt: while let Some(_) = None::<i32> { break; } | ^^^ lifetime 'lt already in scope error: compilation successful - --> $DIR/loops-reject-duplicate-labels.rs:49:1 + --> $DIR/loops-reject-duplicate-labels.rs:50:1 | LL | / pub fn main() { //~ ERROR compilation successful LL | | let s = S; diff --git a/src/test/ui/nll/get_default.nll.stderr b/src/test/ui/nll/get_default.nll.stderr index c6f021f8c36..b955a51e38d 100644 --- a/src/test/ui/nll/get_default.nll.stderr +++ b/src/test/ui/nll/get_default.nll.stderr @@ -4,14 +4,14 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as imm LL | match map.get() { | --- immutable borrow occurs here ... -LL | map.set(String::new()); // Just AST errors here +LL | map.set(String::new()); // Ideally, this would not error. | ^^^ mutable borrow occurs here ... LL | } | - immutable borrow ends here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) - --> $DIR/get_default.rs:44:17 + --> $DIR/get_default.rs:45:17 | LL | match map.get() { | --- immutable borrow occurs here @@ -23,19 +23,40 @@ LL | } | - immutable borrow ends here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) - --> $DIR/get_default.rs:50:17 + --> $DIR/get_default.rs:51:17 | LL | match map.get() { | --- immutable borrow occurs here ... -LL | map.set(String::new()); // Just AST errors here +LL | map.set(String::new()); // Ideally, just AST would error here | ^^^ mutable borrow occurs here ... LL | } | - immutable borrow ends here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) - --> $DIR/get_default.rs:44:17 + --> $DIR/get_default.rs:33:17 + | +LL | match map.get() { + | --- immutable borrow occurs here +... +LL | map.set(String::new()); // Ideally, this would not error. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 26:1... + --> $DIR/get_default.rs:26:1 + | +LL | / fn ok(map: &mut Map) -> &String { +LL | | loop { +LL | | match map.get() { +LL | | Some(v) => { +... | +LL | | } +LL | | } + | |_^ + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) + --> $DIR/get_default.rs:45:17 | LL | match map.get() { | --- immutable borrow occurs here @@ -46,6 +67,27 @@ LL | map.set(String::new()); // Both AST and MIR error here LL | return v; | - borrow later used here -error: aborting due to 4 previous errors +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) + --> $DIR/get_default.rs:51:17 + | +LL | match map.get() { + | --- immutable borrow occurs here +... +LL | map.set(String::new()); // Ideally, just AST would error here + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1... + --> $DIR/get_default.rs:41:1 + | +LL | / fn err(map: &mut Map) -> &String { +LL | | loop { +LL | | match map.get() { +LL | | Some(v) => { +... | +LL | | } +LL | | } + | |_^ + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/nll/get_default.rs b/src/test/ui/nll/get_default.rs index 728c84695ea..1a417b1e28c 100644 --- a/src/test/ui/nll/get_default.rs +++ b/src/test/ui/nll/get_default.rs @@ -30,8 +30,9 @@ fn ok(map: &mut Map) -> &String { return v; } None => { - map.set(String::new()); // Just AST errors here + map.set(String::new()); // Ideally, this would not error. //~^ ERROR borrowed as immutable (Ast) + //~| ERROR borrowed as immutable (Mir) } } } @@ -47,8 +48,9 @@ fn err(map: &mut Map) -> &String { return v; } None => { - map.set(String::new()); // Just AST errors here + map.set(String::new()); // Ideally, just AST would error here //~^ ERROR borrowed as immutable (Ast) + //~| ERROR borrowed as immutable (Mir) } } } diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr index 064fd38b872..dd69e18652c 100644 --- a/src/test/ui/nll/get_default.stderr +++ b/src/test/ui/nll/get_default.stderr @@ -4,14 +4,14 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as imm LL | match map.get() { | --- immutable borrow occurs here ... -LL | map.set(String::new()); // Just AST errors here +LL | map.set(String::new()); // Ideally, this would not error. | ^^^ mutable borrow occurs here ... LL | } | - immutable borrow ends here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) - --> $DIR/get_default.rs:44:17 + --> $DIR/get_default.rs:45:17 | LL | match map.get() { | --- immutable borrow occurs here @@ -23,19 +23,61 @@ LL | } | - immutable borrow ends here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) - --> $DIR/get_default.rs:50:17 + --> $DIR/get_default.rs:51:17 | LL | match map.get() { | --- immutable borrow occurs here ... -LL | map.set(String::new()); // Just AST errors here +LL | map.set(String::new()); // Ideally, just AST would error here | ^^^ mutable borrow occurs here ... LL | } | - immutable borrow ends here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) - --> $DIR/get_default.rs:44:17 + --> $DIR/get_default.rs:33:17 + | +LL | match map.get() { + | --- immutable borrow occurs here +... +LL | map.set(String::new()); // Ideally, this would not error. + | ^^^ mutable borrow occurs here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 26:1... + --> $DIR/get_default.rs:26:1 + | +LL | / fn ok(map: &mut Map) -> &String { +LL | | loop { +LL | | match map.get() { +LL | | Some(v) => { +... | +LL | | } +LL | | } + | |_^ + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) + --> $DIR/get_default.rs:51:17 + | +LL | match map.get() { + | --- immutable borrow occurs here +... +LL | map.set(String::new()); // Ideally, just AST would error here + | ^^^ mutable borrow occurs here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1... + --> $DIR/get_default.rs:41:1 + | +LL | / fn err(map: &mut Map) -> &String { +LL | | loop { +LL | | match map.get() { +LL | | Some(v) => { +... | +LL | | } +LL | | } + | |_^ + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) + --> $DIR/get_default.rs:45:17 | LL | match map.get() { | --- immutable borrow occurs here @@ -46,6 +88,6 @@ LL | map.set(String::new()); // Both AST and MIR error here LL | return v; | - borrow later used here -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/suggestions/suggest-labels.rs b/src/test/ui/suggestions/suggest-labels.rs index 8c97301f40b..9fb519c57ed 100644 --- a/src/test/ui/suggestions/suggest-labels.rs +++ b/src/test/ui/suggestions/suggest-labels.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[allow(unreachable_code)] +#[allow(unreachable_code, unused_labels)] fn main() { 'foo: loop { break 'fo; //~ ERROR use of undeclared label diff --git a/src/tools/clippy b/src/tools/clippy -Subproject c658fc8cbcd1f199edd445a49cb43139ebdc5f0 +Subproject ebe0b0eed596243a2839867363cb31d93f0b975 diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 812e9c5f39d..b2ce5ce52f7 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -118,6 +118,9 @@ impl CompareMode { #[derive(Clone)] pub struct Config { + /// Whether to overwrite stderr/stdout files instead of complaining about changes in output + pub bless: bool, + /// The library paths required for running the compiler pub compile_lib_path: PathBuf, diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 42a2cdfa55b..2bfc1ece095 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -168,6 +168,11 @@ pub fn parse_config(args: Vec<String>) -> Config { .optflag("", "verbose", "run tests verbosely, showing all output") .optflag( "", + "bless", + "overwrite stderr/stdout files instead of complaining about a mismatch", + ) + .optflag( + "", "quiet", "print one character per test instead of one line", ) @@ -290,6 +295,7 @@ pub fn parse_config(args: Vec<String>) -> Config { let src_base = opt_path(matches, "src-base"); let run_ignored = matches.opt_present("ignored"); Config { + bless: matches.opt_present("bless"), compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), rustc_path: opt_path(matches, "rustc-path"), diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 780c8122734..140c90aaeac 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2502,7 +2502,7 @@ impl<'test> TestCx<'test> { .env("IS_WINDOWS", "1") .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) .env("CC", format!("'{}' {}", self.config.cc, cflags)) - .env("CXX", format!("'{}'", &self.config.cxx)); + .env("CXX", &self.config.cxx); } else { cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags)) .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags)) @@ -2596,15 +2596,13 @@ impl<'test> TestCx<'test> { } if errors > 0 { - println!("To update references, run this command from build directory:"); + println!("To update references, rerun the tests and pass the `--bless` flag"); let relative_path_to_file = self.testpaths .relative_dir .join(self.testpaths.file.file_name().unwrap()); println!( - "{}/update-references.sh '{}' '{}'", - self.config.src_base.display(), - self.config.build_base.display(), - relative_path_to_file.display() + "To only update this specific test, also pass `--test-args {}`", + relative_path_to_file.display(), ); self.fatal_proc_rec( &format!("{} errors occurred comparing output.", errors), @@ -2926,29 +2924,31 @@ impl<'test> TestCx<'test> { return 0; } - if expected.is_empty() { - println!("normalized {}:\n{}\n", kind, actual); - } else { - println!("diff of {}:\n", kind); - let diff_results = make_diff(expected, actual, 3); - for result in diff_results { - let mut line_number = result.line_number; - for line in result.lines { - match line { - DiffLine::Expected(e) => { - println!("-\t{}", e); - line_number += 1; - } - DiffLine::Context(c) => { - println!("{}\t{}", line_number, c); - line_number += 1; - } - DiffLine::Resulting(r) => { - println!("+\t{}", r); + if !self.config.bless { + if expected.is_empty() { + println!("normalized {}:\n{}\n", kind, actual); + } else { + println!("diff of {}:\n", kind); + let diff_results = make_diff(expected, actual, 3); + for result in diff_results { + let mut line_number = result.line_number; + for line in result.lines { + match line { + DiffLine::Expected(e) => { + println!("-\t{}", e); + line_number += 1; + } + DiffLine::Context(c) => { + println!("{}\t{}", line_number, c); + line_number += 1; + } + DiffLine::Resulting(r) => { + println!("+\t{}", r); + } } } + println!(""); } - println!(""); } } @@ -2958,19 +2958,47 @@ impl<'test> TestCx<'test> { .with_extra_extension(mode) .with_extra_extension(kind); - match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) { - Ok(()) => {} - Err(e) => self.fatal(&format!( - "failed to write {} to `{}`: {}", + let mut files = vec![output_file]; + if self.config.bless { + files.push(expected_output_path( + self.testpaths, + self.revision, + &self.config.compare_mode, kind, - output_file.display(), - e - )), + )); + } + + for output_file in &files { + if actual.is_empty() { + if let Err(e) = ::std::fs::remove_file(output_file) { + self.fatal(&format!( + "failed to delete `{}`: {}", + output_file.display(), + e, + )); + } + } else { + match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) { + Ok(()) => {} + Err(e) => self.fatal(&format!( + "failed to write {} to `{}`: {}", + kind, + output_file.display(), + e + )), + } + } } println!("\nThe actual {0} differed from the expected {0}.", kind); - println!("Actual {} saved to {}", kind, output_file.display()); - 1 + for output_file in files { + println!("Actual {} saved to {}", kind, output_file.display()); + } + if self.config.bless { + 0 + } else { + 1 + } } fn create_stamp(&self) { diff --git a/src/tools/rls b/src/tools/rls -Subproject 3e3df0485004bc1343bc8200b68c67ac7c479b2 +Subproject cf0609d0af0b734d4b9ee9dce6df66f946fc763 diff --git a/src/tools/rustdoc/Cargo.toml b/src/tools/rustdoc/Cargo.toml index 344f617ef95..d3881500441 100644 --- a/src/tools/rustdoc/Cargo.toml +++ b/src/tools/rustdoc/Cargo.toml @@ -7,7 +7,7 @@ authors = ["The Rust Project Developers"] # the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" # rustdoc a different name. [[bin]] -name = "rustdoc-tool-binary" +name = "rustdoc_tool_binary" path = "main.rs" [dependencies] diff --git a/src/tools/rustfmt b/src/tools/rustfmt -Subproject db8cb0b8d6942d42a322b1d36b2504977404f36 +Subproject bf2581bf7709b91c4431ba7074de910f72283e1 |
