From 4d92fe2bb07a12368b6bb981d08f12f4a446a4d3 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 8 Feb 2018 16:26:43 -0800 Subject: [PATCH 01/42] Fix span bug. --- src/libsyntax/parse/parser.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index dc3745fc4a3ee..c7de0a75817c5 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2630,8 +2630,7 @@ impl<'a> Parser<'a> { // A tuple index may not have a suffix self.expect_no_suffix(sp, "tuple index", suf); - let dot_span = self.prev_span; - hi = self.span; + let idx_span = self.span; self.bump(); let invalid_msg = "invalid tuple or struct index"; @@ -2646,9 +2645,8 @@ impl<'a> Parser<'a> { n.to_string()); err.emit(); } - let id = respan(dot_span.to(hi), n); - let field = self.mk_tup_field(e, id); - e = self.mk_expr(lo.to(hi), field, ThinVec::new()); + let field = self.mk_tup_field(e, respan(idx_span, n)); + e = self.mk_expr(lo.to(idx_span), field, ThinVec::new()); } None => { let prev_span = self.prev_span; From 50642c877a73f4b304c5771d9dc77eaf2e5d20a5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 26 Aug 2017 18:30:12 -0700 Subject: [PATCH 02/42] rust: Import LLD for linking wasm objects This commit imports the LLD project from LLVM to serve as the default linker for the `wasm32-unknown-unknown` target. The `binaryen` submoule is consequently removed along with "binaryen linker" support in rustc. Moving to LLD brings with it a number of benefits for wasm code: * LLD is itself an actual linker, so there's no need to compile all wasm code with LTO any more. As a result builds should be *much* speedier as LTO is no longer forcibly enabled for all builds of the wasm target. * LLD is quickly becoming an "official solution" for linking wasm code together. This, I believe at least, is intended to be the main supported linker for native code and wasm moving forward. Picking up support early on should help ensure that we can help LLD identify bugs and otherwise prove that it works great for all our use cases! * Improvements to the wasm toolchain are currently primarily focused around LLVM and LLD (from what I can tell at least), so it's in general much better to be on this bandwagon for bugfixes and new features. * Historical "hacks" like `wasm-gc` will soon no longer be necessary, LLD will [natively implement][gc] `--gc-sections` (better than `wasm-gc`!) which means a postprocessor is no longer needed to show off Rust's "small wasm binary size". LLD is added in a pretty standard way to rustc right now. A new rustbuild target was defined for building LLD, and this is executed when a compiler's sysroot is being assembled. LLD is compiled against the LLVM that we've got in tree, which means we're currently on the `release_60` branch, but this may get upgraded in the near future! LLD is placed into rustc's sysroot in a `bin` directory. This is similar to where `gcc.exe` can be found on Windows. This directory is automatically added to `PATH` whenever rustc executes the linker, allowing us to define a `WasmLd` linker which implements the interface that `wasm-ld`, LLD's frontend, expects. Like Emscripten the LLD target is currently only enabled for Tier 1 platforms, notably OSX/Windows/Linux, and will need to be installed manually for compiling to wasm on other platforms. LLD is by default turned off in rustbuild, and requires a `config.toml` option to be enabled to turn it on. Finally the unstable `#![wasm_import_memory]` attribute was also removed as LLD has a native option for controlling this. [gc]: https://reviews.llvm.org/D42511 --- .gitmodules | 6 +- .travis.yml | 4 +- README.md | 3 - appveyor.yml | 10 +- config.toml.example | 4 + src/Cargo.lock | 10 - src/binaryen | 1 - src/bootstrap/bin/rustc.rs | 2 +- src/bootstrap/bootstrap.py | 4 + src/bootstrap/builder.rs | 2 +- src/bootstrap/cc_detect.rs | 3 + src/bootstrap/compile.rs | 26 +++ src/bootstrap/config.rs | 4 + src/bootstrap/configure.py | 5 + src/bootstrap/dist.rs | 18 +- src/bootstrap/lib.rs | 8 +- src/bootstrap/native.rs | 194 ++++++++++++------ src/bootstrap/test.rs | 2 +- src/ci/docker/dist-i686-linux/Dockerfile | 3 +- src/ci/docker/dist-x86_64-linux/Dockerfile | 5 +- src/ci/docker/wasm32-unknown/Dockerfile | 3 +- src/etc/wasm32-shim.js | 2 + src/librustc_back/lib.rs | 54 +++-- .../target/wasm32_unknown_unknown.rs | 55 +---- src/librustc_binaryen/BinaryenWrapper.cpp | 160 --------------- src/librustc_binaryen/Cargo.toml | 16 -- src/librustc_binaryen/build.rs | 60 ------ src/librustc_binaryen/lib.rs | 172 ---------------- src/librustc_trans/Cargo.toml | 1 - src/librustc_trans/back/command.rs | 14 ++ src/librustc_trans/back/link.rs | 39 +--- src/librustc_trans/back/linker.rs | 114 +++++++++- src/librustc_trans/back/symbol_export.rs | 17 +- src/librustc_trans/back/write.rs | 85 +------- src/librustc_trans/lib.rs | 1 - src/libsyntax/feature_gate.rs | 8 - src/test/run-pass/issue-15487.rs | 1 + .../ui/feature-gate-wasm_import_memory.rs | 14 -- .../ui/feature-gate-wasm_import_memory.stderr | 10 - src/tools/lld | 1 + src/tools/tidy/src/lib.rs | 2 +- 41 files changed, 402 insertions(+), 741 deletions(-) delete mode 160000 src/binaryen delete mode 100644 src/librustc_binaryen/BinaryenWrapper.cpp delete mode 100644 src/librustc_binaryen/Cargo.toml delete mode 100644 src/librustc_binaryen/build.rs delete mode 100644 src/librustc_binaryen/lib.rs delete mode 100644 src/test/ui/feature-gate-wasm_import_memory.rs delete mode 100644 src/test/ui/feature-gate-wasm_import_memory.stderr create mode 160000 src/tools/lld diff --git a/.gitmodules b/.gitmodules index 65aafeea17bd9..f21cd6aae2395 100644 --- a/.gitmodules +++ b/.gitmodules @@ -45,12 +45,12 @@ [submodule "src/dlmalloc"] path = src/dlmalloc url = https://github.com/alexcrichton/dlmalloc-rs.git -[submodule "src/binaryen"] - path = src/binaryen - url = https://github.com/alexcrichton/binaryen.git [submodule "src/doc/rust-by-example"] path = src/doc/rust-by-example url = https://github.com/rust-lang/rust-by-example [submodule "src/llvm-emscripten"] path = src/llvm-emscripten url = https://github.com/rust-lang/llvm +[submodule "src/tools/lld"] + path = src/tools/lld + url = https://github.com/rust-lang/lld.git diff --git a/.travis.yml b/.travis.yml index 1007aad925d96..d6552c24a2ae1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -81,7 +81,7 @@ matrix: # OSX 10.7 and `xcode7` is the latest Xcode able to compile LLVM for 10.7. - env: > RUST_CHECK_TARGET=dist - RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-extended --enable-profiler --enable-emscripten" + RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-full-tools --enable-profiler" SRC=. DEPLOY=1 RUSTC_RETRY_LINKER_ON_SEGFAULT=1 @@ -95,7 +95,7 @@ matrix: - env: > RUST_CHECK_TARGET=dist - RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended --enable-sanitizers --enable-profiler --enable-emscripten" + RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler" SRC=. DEPLOY=1 RUSTC_RETRY_LINKER_ON_SEGFAULT=1 diff --git a/README.md b/README.md index 589aa1afe35ec..4fc003036e9b3 100644 --- a/README.md +++ b/README.md @@ -129,9 +129,6 @@ CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64. python x.py build ``` -If you are seeing build failure when compiling `rustc_binaryen`, make sure the path -length of the rust folder is not longer than 22 characters. - #### Specifying an ABI [specifying-an-abi]: #specifying-an-abi diff --git a/appveyor.yml b/appveyor.yml index 7f1c538a32e46..6d715e799d619 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,21 +61,19 @@ environment: # 32/64 bit MSVC and GNU deployment - RUST_CONFIGURE_ARGS: > --build=x86_64-pc-windows-msvc - --enable-extended + --enable-full-tools --enable-profiler - --enable-emscripten SCRIPT: python x.py dist DEPLOY: 1 - RUST_CONFIGURE_ARGS: > --build=i686-pc-windows-msvc --target=i586-pc-windows-msvc - --enable-extended + --enable-full-tools --enable-profiler - --enable-emscripten SCRIPT: python x.py dist DEPLOY: 1 - MSYS_BITS: 32 - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-extended --enable-emscripten + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-full-tools SCRIPT: python x.py dist MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror MINGW_ARCHIVE: i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z @@ -83,7 +81,7 @@ environment: DEPLOY: 1 - MSYS_BITS: 64 SCRIPT: python x.py dist - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-extended --enable-emscripten + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-full-tools MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror MINGW_ARCHIVE: x86_64-6.3.0-release-posix-seh-rt_v5-rev2.7z MINGW_DIR: mingw64 diff --git a/config.toml.example b/config.toml.example index f153562a53894..d8a3aaff9aa79 100644 --- a/config.toml.example +++ b/config.toml.example @@ -326,6 +326,10 @@ # target, as without this option the test output will not be captured. #wasm-syscall = false +# Indicates whether LLD will be compiled and made available in the sysroot for +# rustc to execute. +#lld = false + # ============================================================================= # Options for specific targets # diff --git a/src/Cargo.lock b/src/Cargo.lock index afe7f841f2571..cfd1ba5e4af1d 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1863,15 +1863,6 @@ dependencies = [ "syntax 0.0.0", ] -[[package]] -name = "rustc_binaryen" -version = "0.0.0" -dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rustc_borrowck" version = "0.0.0" @@ -2148,7 +2139,6 @@ dependencies = [ "rustc_allocator 0.0.0", "rustc_apfloat 0.0.0", "rustc_back 0.0.0", - "rustc_binaryen 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", diff --git a/src/binaryen b/src/binaryen deleted file mode 160000 index 17841e155edf8..0000000000000 --- a/src/binaryen +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 17841e155edf858c8ea7802dd5f5ecbef54b989f diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 55d104b182698..d583213750f45 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -219,7 +219,7 @@ fn main() { // flesh out rpath support more fully in the future. cmd.arg("-Z").arg("osx-rpath-install-name"); Some("-Wl,-rpath,@loader_path/../lib") - } else if !target.contains("windows") { + } else if !target.contains("windows") && !target.contains("wasm32") { Some("-Wl,-rpath,$ORIGIN/../lib") } else { None diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 603a97ddfd412..9cf1bd285364a 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -654,6 +654,10 @@ def update_submodules(self): continue if self.get_toml('jemalloc'): continue + if module.endswith("lld"): + config = self.get_toml('lld') + if config is None or config == 'false': + continue filtered_submodules.append(module) run(["git", "submodule", "update", "--init", "--recursive"] + filtered_submodules, diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 74dd4a6fa0144..3bdafe4944a03 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -252,7 +252,7 @@ impl<'a> Builder<'a> { tool::UnstableBookGen, tool::Tidy, tool::Linkchecker, tool::CargoTest, tool::Compiletest, tool::RemoteTestServer, tool::RemoteTestClient, tool::RustInstaller, tool::Cargo, tool::Rls, tool::Rustdoc, tool::Clippy, - native::Llvm, tool::Rustfmt, tool::Miri), + native::Llvm, tool::Rustfmt, tool::Miri, native::Lld), Kind::Check => describe!(check::Std, check::Test, check::Rustc), Kind::Test => describe!(test::Tidy, test::Bootstrap, test::DefaultCompiletest, test::HostCompiletest, test::Crate, test::CrateLibrustc, test::Rustdoc, diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs index e531fdaf2923b..9e1b1f7db2f99 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -79,6 +79,9 @@ pub fn find(build: &mut Build) { let mut cfg = cc::Build::new(); cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false) .target(&target).host(&build.build); + if target.contains("msvc") { + cfg.static_crt(true); + } let config = build.config.target_config.get(&target); if let Some(cc) = config.and_then(|c| c.cc.as_ref()) { diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 1d5e11c5d6d41..fe8b1c16279a5 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -746,6 +746,21 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder, } } +fn copy_lld_to_sysroot(builder: &Builder, + target_compiler: Compiler, + lld_install_root: &Path) { + let target = target_compiler.host; + + let dst = builder.sysroot_libdir(target_compiler, target) + .parent() + .unwrap() + .join("bin"); + t!(fs::create_dir_all(&dst)); + + let exe = exe("lld", &target); + copy(&lld_install_root.join("bin").join(&exe), &dst.join(&exe)); +} + /// Cargo's output path for the standard library in a given stage, compiled /// by a particular compiler for the specified target. pub fn libstd_stamp(build: &Build, compiler: Compiler, target: Interned) -> PathBuf { @@ -895,6 +910,14 @@ impl Step for Assemble { } } + let lld_install = if build.config.lld_enabled && target_compiler.stage > 0 { + Some(builder.ensure(native::Lld { + target: target_compiler.host, + })) + } else { + None + }; + let stage = target_compiler.stage; let host = target_compiler.host; println!("Assembling stage{} compiler ({})", stage, host); @@ -914,6 +937,9 @@ impl Step for Assemble { copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler); + if let Some(lld_install) = lld_install { + copy_lld_to_sysroot(builder, target_compiler, &lld_install); + } // Link the compiler binary itself into place let out_dir = build.cargo_out(build_compiler, Mode::Librustc, host); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 4f4fd14ae8cab..d746dfd2a91e7 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -79,6 +79,8 @@ pub struct Config { pub llvm_experimental_targets: String, pub llvm_link_jobs: Option, + pub lld_enabled: bool, + // rust codegen options pub rust_optimize: bool, pub rust_codegen_units: Option, @@ -288,6 +290,7 @@ struct Rust { save_toolstates: Option, codegen_backends: Option>, wasm_syscall: Option, + lld: Option, } /// TOML representation of how each build target is configured. @@ -473,6 +476,7 @@ impl Config { set(&mut config.quiet_tests, rust.quiet_tests); set(&mut config.test_miri, rust.test_miri); set(&mut config.wasm_syscall, rust.wasm_syscall); + set(&mut config.lld_enabled, rust.lld); config.rustc_parallel_queries = rust.experimental_parallel_queries.unwrap_or(false); config.rustc_default_linker = rust.default_linker.clone(); config.musl_root = rust.musl_root.clone().map(PathBuf::from); diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 99a3ee4e4c369..e9b4a233d0af5 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -66,6 +66,7 @@ def v(*args): o("cargo-openssl-static", "build.openssl-static", "static openssl in cargo") o("profiler", "build.profiler", "build the profiler runtime") o("emscripten", None, "compile the emscripten backend as well as LLVM") +o("full-tools", None, "enable all tools") # Optimization and debugging options. These may be overridden by the release # channel, etc. @@ -326,6 +327,10 @@ def set(key, value): set('build.target', value.split(',')) elif option.name == 'emscripten': set('rust.codegen-backends', ['llvm', 'emscripten']) + elif option.name == 'full-tools': + set('rust.codegen-backends', ['llvm', 'emscripten']) + set('rust.lld', True) + set('build.extended', True) elif option.name == 'option-checking': # this was handled above pass diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 460fb016f16ea..d93b49f01d0b0 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -28,7 +28,7 @@ use build_helper::output; use {Build, Compiler, Mode}; use channel; -use util::{cp_r, libdir, is_dylib, cp_filtered, copy, replace_in_file}; +use util::{cp_r, libdir, is_dylib, cp_filtered, copy, replace_in_file, exe}; use builder::{Builder, RunConfig, ShouldRun, Step}; use compile; use native; @@ -443,6 +443,22 @@ impl Step for Rustc { t!(fs::create_dir_all(&backends_dst)); cp_r(&backends_src, &backends_dst); + // Copy over lld if it's there + if builder.config.lld_enabled { + let exe = exe("lld", &compiler.host); + let src = builder.sysroot_libdir(compiler, host) + .parent() + .unwrap() + .join("bin") + .join(&exe); + let dst = image.join("lib/rustlib") + .join(&*host) + .join("bin") + .join(&exe); + t!(fs::create_dir_all(&dst.parent().unwrap())); + copy(&src, &dst); + } + // Man pages t!(fs::create_dir_all(image.join("share/man/man1"))); let man_src = build.src.join("src/doc/man"); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 83c270865c0b7..1ca1094c2fd2b 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -488,6 +488,10 @@ impl Build { self.out.join(&*target).join("llvm-emscripten") } + fn lld_out(&self, target: Interned) -> PathBuf { + self.out.join(&*target).join("lld") + } + /// Output directory for all documentation for a target fn doc_out(&self, target: Interned) -> PathBuf { self.out.join(&*target).join("doc") @@ -672,7 +676,9 @@ impl Build { .and_then(|c| c.linker.as_ref()) { Some(linker) } else if target != self.config.build && - !target.contains("msvc") && !target.contains("emscripten") { + !target.contains("msvc") && + !target.contains("emscripten") && + !target.contains("wasm32") { Some(self.cc(target)) } else { None diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 2ea026244034f..63cb958008e61 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -112,9 +112,6 @@ impl Step for Llvm { // http://llvm.org/docs/CMake.html let root = if self.emscripten { "src/llvm-emscripten" } else { "src/llvm" }; let mut cfg = cmake::Config::new(build.src.join(root)); - if build.config.ninja { - cfg.generator("Ninja"); - } let profile = match (build.config.llvm_optimize, build.config.llvm_release_debuginfo) { (false, _) => "Debug", @@ -141,9 +138,7 @@ impl Step for Llvm { let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"}; - cfg.target(&target) - .host(&build.build) - .out_dir(&out_dir) + cfg.out_dir(&out_dir) .profile(profile) .define("LLVM_ENABLE_ASSERTIONS", assertions) .define("LLVM_TARGETS_TO_BUILD", llvm_targets) @@ -207,67 +202,7 @@ impl Step for Llvm { cfg.define("LLVM_NATIVE_BUILD", build.llvm_out(build.build).join("build")); } - let sanitize_cc = |cc: &Path| { - if target.contains("msvc") { - OsString::from(cc.to_str().unwrap().replace("\\", "/")) - } else { - cc.as_os_str().to_owned() - } - }; - - let configure_compilers = |cfg: &mut cmake::Config| { - // MSVC with CMake uses msbuild by default which doesn't respect these - // vars that we'd otherwise configure. In that case we just skip this - // entirely. - if target.contains("msvc") && !build.config.ninja { - return - } - - let cc = build.cc(target); - let cxx = build.cxx(target).unwrap(); - - // Handle msvc + ninja + ccache specially (this is what the bots use) - if target.contains("msvc") && - build.config.ninja && - build.config.ccache.is_some() { - let mut cc = env::current_exe().expect("failed to get cwd"); - cc.set_file_name("sccache-plus-cl.exe"); - - cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc)) - .define("CMAKE_CXX_COMPILER", sanitize_cc(&cc)); - cfg.env("SCCACHE_PATH", - build.config.ccache.as_ref().unwrap()) - .env("SCCACHE_TARGET", target); - - // If ccache is configured we inform the build a little differently hwo - // to invoke ccache while also invoking our compilers. - } else if let Some(ref ccache) = build.config.ccache { - cfg.define("CMAKE_C_COMPILER", ccache) - .define("CMAKE_C_COMPILER_ARG1", sanitize_cc(cc)) - .define("CMAKE_CXX_COMPILER", ccache) - .define("CMAKE_CXX_COMPILER_ARG1", sanitize_cc(cxx)); - } else { - cfg.define("CMAKE_C_COMPILER", sanitize_cc(cc)) - .define("CMAKE_CXX_COMPILER", sanitize_cc(cxx)); - } - - cfg.build_arg("-j").build_arg(build.jobs().to_string()); - cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" ")); - cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" ")); - if let Some(ar) = build.ar(target) { - if ar.is_absolute() { - // LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it - // tries to resolve this path in the LLVM build directory. - cfg.define("CMAKE_AR", sanitize_cc(ar)); - } - } - }; - - configure_compilers(&mut cfg); - - if env::var_os("SCCACHE_ERROR_LOG").is_some() { - cfg.env("RUST_LOG", "sccache=warn"); - } + configure_cmake(build, target, &mut cfg); // FIXME: we don't actually need to build all LLVM tools and all LLVM // libraries here, e.g. we just want a few components and a few @@ -298,6 +233,131 @@ fn check_llvm_version(build: &Build, llvm_config: &Path) { panic!("\n\nbad LLVM version: {}, need >=3.9\n\n", version) } +fn configure_cmake(build: &Build, + target: Interned, + cfg: &mut cmake::Config) { + if build.config.ninja { + cfg.generator("Ninja"); + } + cfg.target(&target) + .host(&build.config.build); + + let sanitize_cc = |cc: &Path| { + if target.contains("msvc") { + OsString::from(cc.to_str().unwrap().replace("\\", "/")) + } else { + cc.as_os_str().to_owned() + } + }; + + // MSVC with CMake uses msbuild by default which doesn't respect these + // vars that we'd otherwise configure. In that case we just skip this + // entirely. + if target.contains("msvc") && !build.config.ninja { + return + } + + let cc = build.cc(target); + let cxx = build.cxx(target).unwrap(); + + // Handle msvc + ninja + ccache specially (this is what the bots use) + if target.contains("msvc") && + build.config.ninja && + build.config.ccache.is_some() { + let mut cc = env::current_exe().expect("failed to get cwd"); + cc.set_file_name("sccache-plus-cl.exe"); + + cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc)) + .define("CMAKE_CXX_COMPILER", sanitize_cc(&cc)); + cfg.env("SCCACHE_PATH", + build.config.ccache.as_ref().unwrap()) + .env("SCCACHE_TARGET", target); + + // If ccache is configured we inform the build a little differently hwo + // to invoke ccache while also invoking our compilers. + } else if let Some(ref ccache) = build.config.ccache { + cfg.define("CMAKE_C_COMPILER", ccache) + .define("CMAKE_C_COMPILER_ARG1", sanitize_cc(cc)) + .define("CMAKE_CXX_COMPILER", ccache) + .define("CMAKE_CXX_COMPILER_ARG1", sanitize_cc(cxx)); + } else { + cfg.define("CMAKE_C_COMPILER", sanitize_cc(cc)) + .define("CMAKE_CXX_COMPILER", sanitize_cc(cxx)); + } + + cfg.build_arg("-j").build_arg(build.jobs().to_string()); + cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" ")); + let mut cxxflags = build.cflags(target).join(" "); + if build.config.llvm_static_stdcpp && !target.contains("windows") { + cxxflags.push_str(" -static-libstdc++"); + } + cfg.define("CMAKE_CXX_FLAGS", cxxflags); + if let Some(ar) = build.ar(target) { + if ar.is_absolute() { + // LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it + // tries to resolve this path in the LLVM build directory. + cfg.define("CMAKE_AR", sanitize_cc(ar)); + } + } + + if env::var_os("SCCACHE_ERROR_LOG").is_some() { + cfg.env("RUST_LOG", "sccache=warn"); + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct Lld { + pub target: Interned, +} + +impl Step for Lld { + type Output = PathBuf; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun) -> ShouldRun { + run.path("src/tools/lld") + } + + fn make_run(run: RunConfig) { + run.builder.ensure(Lld { target: run.target }); + } + + /// Compile LLVM for `target`. + fn run(self, builder: &Builder) -> PathBuf { + let target = self.target; + let build = builder.build; + + let llvm_config = builder.ensure(Llvm { + target: self.target, + emscripten: false, + }); + + let out_dir = build.lld_out(target); + let done_stamp = out_dir.join("lld-finished-building"); + if done_stamp.exists() { + return out_dir + } + + let _folder = build.fold_output(|| "lld"); + println!("Building LLD for {}", target); + let _time = util::timeit(); + t!(fs::create_dir_all(&out_dir)); + + let mut cfg = cmake::Config::new(build.src.join("src/tools/lld")); + configure_cmake(build, target, &mut cfg); + + cfg.out_dir(&out_dir) + .profile("Release") + .define("LLVM_CONFIG_PATH", llvm_config) + .define("LLVM_INCLUDE_TESTS", "OFF"); + + cfg.build(); + + t!(File::create(&done_stamp)); + out_dir + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct TestHelpers { pub target: Interned, diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index f6b95f0bf9744..d6b363b99080f 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -879,7 +879,7 @@ impl Step for Compiletest { } if build.config.llvm_enabled { - let llvm_config = build.llvm_config(target); + let llvm_config = build.llvm_config(build.config.build); let llvm_version = output(Command::new(&llvm_config).arg("--version")); cmd.arg("--llvm-version").arg(llvm_version); if !build.is_rust_llvm(target) { diff --git a/src/ci/docker/dist-i686-linux/Dockerfile b/src/ci/docker/dist-i686-linux/Dockerfile index 5e405aa72e83d..829166d2237a6 100644 --- a/src/ci/docker/dist-i686-linux/Dockerfile +++ b/src/ci/docker/dist-i686-linux/Dockerfile @@ -83,10 +83,9 @@ ENV HOSTS=i686-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS \ --host=$HOSTS \ - --enable-extended \ + --enable-full-tools \ --enable-sanitizers \ --enable-profiler \ - --enable-emscripten \ --build=i686-unknown-linux-gnu ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/dist-x86_64-linux/Dockerfile b/src/ci/docker/dist-x86_64-linux/Dockerfile index d368a00b55bd5..f5c24bf508651 100644 --- a/src/ci/docker/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/dist-x86_64-linux/Dockerfile @@ -83,10 +83,9 @@ ENV HOSTS=x86_64-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS \ --host=$HOSTS \ - --enable-extended \ + --enable-full-tools \ --enable-sanitizers \ - --enable-profiler \ - --enable-emscripten + --enable-profiler ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS # This is the only builder which will create source tarballs diff --git a/src/ci/docker/wasm32-unknown/Dockerfile b/src/ci/docker/wasm32-unknown/Dockerfile index dc1727b7014c3..3965d2d40c1df 100644 --- a/src/ci/docker/wasm32-unknown/Dockerfile +++ b/src/ci/docker/wasm32-unknown/Dockerfile @@ -23,7 +23,8 @@ ENV TARGETS=wasm32-unknown-unknown ENV RUST_CONFIGURE_ARGS \ --target=$TARGETS \ - --set build.nodejs=/node-v9.2.0-linux-x64/bin/node + --set build.nodejs=/node-v9.2.0-linux-x64/bin/node \ + --set rust.lld ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \ src/test/ui \ diff --git a/src/etc/wasm32-shim.js b/src/etc/wasm32-shim.js index 69647f37eecc6..98d6202a63fbf 100644 --- a/src/etc/wasm32-shim.js +++ b/src/etc/wasm32-shim.js @@ -107,6 +107,8 @@ imports.env = { exp2f: function(x) { return Math.pow(2, x); }, ldexp: function(x, y) { return x * Math.pow(2, y); }, ldexpf: function(x, y) { return x * Math.pow(2, y); }, + log: Math.log, + log2: Math.log2, log10: Math.log10, log10f: Math.log10, diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 8bf60b091a7ad..141c8954a3306 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -43,14 +43,29 @@ use std::str::FromStr; use serialize::json::{Json, ToJson}; -macro_rules! linker_flavor { - ($(($variant:ident, $string:expr),)+) => { - #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, - RustcEncodable, RustcDecodable)] - pub enum LinkerFlavor { - $($variant,)+ - } +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, + RustcEncodable, RustcDecodable)] +pub enum LinkerFlavor { + Em, + Gcc, + Ld, + Msvc, + Lld(LldFlavor), +} + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, + RustcEncodable, RustcDecodable)] +pub enum LldFlavor { + Wasm, +} +impl ToJson for LinkerFlavor { + fn to_json(&self) -> Json { + self.desc().to_json() + } +} +macro_rules! flavor_mappings { + ($((($($flavor:tt)*), $string:expr),)*) => ( impl LinkerFlavor { pub const fn one_of() -> &'static str { concat!("one of: ", $($string, " ",)+) @@ -58,32 +73,27 @@ macro_rules! linker_flavor { pub fn from_str(s: &str) -> Option { Some(match s { - $($string => LinkerFlavor::$variant,)+ + $($string => $($flavor)*,)+ _ => return None, }) } pub fn desc(&self) -> &str { match *self { - $(LinkerFlavor::$variant => $string,)+ + $($($flavor)* => $string,)+ } } } - - impl ToJson for LinkerFlavor { - fn to_json(&self) -> Json { - self.desc().to_json() - } - } - } + ) } -linker_flavor! { - (Em, "em"), - (Binaryen, "binaryen"), - (Gcc, "gcc"), - (Ld, "ld"), - (Msvc, "msvc"), + +flavor_mappings! { + ((LinkerFlavor::Em), "em"), + ((LinkerFlavor::Gcc), "gcc"), + ((LinkerFlavor::Ld), "ld"), + ((LinkerFlavor::Msvc), "msvc"), + ((LinkerFlavor::Lld(LldFlavor::Wasm)), "wasm-ld"), } #[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] diff --git a/src/librustc_back/target/wasm32_unknown_unknown.rs b/src/librustc_back/target/wasm32_unknown_unknown.rs index 242860e5c6e92..c771762cfc89b 100644 --- a/src/librustc_back/target/wasm32_unknown_unknown.rs +++ b/src/librustc_back/target/wasm32_unknown_unknown.rs @@ -8,40 +8,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// The wasm32-unknown-unknown target is currently a highly experimental version -// of a wasm-based target which does *not* use the Emscripten toolchain. Instead -// this is a pretty flavorful (aka hacked up) target right now. The definition -// and semantics of this target are likely to change and so this shouldn't be -// relied on just yet. +// The wasm32-unknown-unknown target is currently an experimental version of a +// wasm-based target which does *not* use the Emscripten toolchain. Instead +// this toolchain is based purely on LLVM's own toolchain, using LLVM's native +// WebAssembly backend as well as LLD for a native linker. // -// In general everyone is currently waiting on a linker for wasm code. In the -// meantime we have no means of actually making use of the traditional separate -// compilation model. At a high level this means that assembling Rust programs -// into a WebAssembly program looks like: -// -// 1. All intermediate artifacts are LLVM bytecode. We'll be using LLVM as -// a linker later on. -// 2. For the final artifact we emit one giant assembly file (WebAssembly -// doesn't have an object file format). To do this we force LTO to be turned -// on (`requires_lto` below) to ensure all Rust code is in one module. Any -// "linked" C library is basically just ignored. -// 3. Using LLVM we emit a `foo.s` file (assembly) with some... what I can only -// describe as arcane syntax. From there we need to actually change this -// into a wasm module. For this step we use the `binaryen` project. This -// project is mostly intended as a WebAssembly code generator, but for now -// we're just using its LLVM-assembly-to-wasm-module conversion utilities. -// -// And voila, out comes a web assembly module! There's some various tweaks here -// and there, but that's the high level at least. Note that this will be -// rethought from the ground up once a linker (lld) is available, so this is all -// temporary and should improve in the future. +// There's some trickery below on crate types supported and various defaults +// (aka panic=abort by default), but otherwise this is in general a relatively +// standard target. -use LinkerFlavor; +use {LinkerFlavor, LldFlavor}; use super::{Target, TargetOptions, PanicStrategy}; pub fn target() -> Result { let opts = TargetOptions { - linker: "not-used".to_string(), + linker: "lld".to_string(), // we allow dynamic linking, but only cdylibs. Basically we allow a // final library artifact that exports some symbols (a wasm module) but @@ -58,9 +39,6 @@ pub fn target() -> Result { dll_suffix: ".wasm".to_string(), linker_is_gnu: false, - // We're storing bitcode for now in all the rlibs - obj_is_bitcode: true, - // A bit of a lie, but "eh" max_atomic_width: Some(32), @@ -69,27 +47,17 @@ pub fn target() -> Result { // the future once unwinding is implemented. Don't rely on this. panic_strategy: PanicStrategy::Abort, - // There's no linker yet so we're forced to use LLVM as a linker. This - // means that we must always enable LTO for final artifacts. - requires_lto: true, - // Wasm doesn't have atomics yet, so tell LLVM that we're in a single // threaded model which will legalize atomics to normal operations. singlethread: true, - // Because we're always enabling LTO we can't enable builtin lowering as - // otherwise we'll lower the definition of the `memcpy` function to - // memcpy itself. Note that this is specifically because we're - // performing LTO with compiler-builtins. - no_builtins: true, - // no dynamic linking, no need for default visibility! default_hidden_visibility: true, .. Default::default() }; Ok(Target { - llvm_target: "wasm32-unknown-unknown".to_string(), + llvm_target: "wasm32-unknown-unknown-wasm".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), target_c_int_width: "32".to_string(), @@ -100,8 +68,7 @@ pub fn target() -> Result { target_vendor: "unknown".to_string(), data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), arch: "wasm32".to_string(), - // A bit of a lie, but it gets the job done - linker_flavor: LinkerFlavor::Binaryen, + linker_flavor: LinkerFlavor::Lld(LldFlavor::Wasm), options: opts, }) } diff --git a/src/librustc_binaryen/BinaryenWrapper.cpp b/src/librustc_binaryen/BinaryenWrapper.cpp deleted file mode 100644 index 55f11665f6d0b..0000000000000 --- a/src/librustc_binaryen/BinaryenWrapper.cpp +++ /dev/null @@ -1,160 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// This is a small C API inserted on top of the Binaryen C++ API which we use -// from Rust. Once we have a real linker for we'll be able to remove all this, -// and otherwise this is just all on a "as we need it" basis for now. - -#include -#include -#include -#include - -#include "s2wasm.h" -#include "wasm-binary.h" -#include "wasm-linker.h" - -using namespace wasm; - -struct BinaryenRustModule { - BufferWithRandomAccess buffer; - std::string sourceMapJSON; -}; - -struct BinaryenRustModuleOptions { - uint64_t globalBase; - bool debug; - uint64_t stackAllocation; - uint64_t initialMem; - uint64_t maxMem; - bool importMemory; - bool ignoreUnknownSymbols; - bool debugInfo; - std::string startFunction; - std::string sourceMapUrl; - - BinaryenRustModuleOptions() : - globalBase(0), - debug(false), - stackAllocation(0), - initialMem(0), - maxMem(0), - importMemory(false), - ignoreUnknownSymbols(false), - debugInfo(false), - startFunction(""), - sourceMapUrl("") - {} - -}; - -extern "C" BinaryenRustModuleOptions* -BinaryenRustModuleOptionsCreate() { - return new BinaryenRustModuleOptions; -} - -extern "C" void -BinaryenRustModuleOptionsFree(BinaryenRustModuleOptions *options) { - delete options; -} - -extern "C" void -BinaryenRustModuleOptionsSetDebugInfo(BinaryenRustModuleOptions *options, - bool debugInfo) { - options->debugInfo = debugInfo; -} - -extern "C" void -BinaryenRustModuleOptionsSetStart(BinaryenRustModuleOptions *options, - char *start) { - options->startFunction = start; -} - -extern "C" void -BinaryenRustModuleOptionsSetSourceMapUrl(BinaryenRustModuleOptions *options, - char *sourceMapUrl) { - options->sourceMapUrl = sourceMapUrl; -} - -extern "C" void -BinaryenRustModuleOptionsSetStackAllocation(BinaryenRustModuleOptions *options, - uint64_t stack) { - options->stackAllocation = stack; -} - -extern "C" void -BinaryenRustModuleOptionsSetImportMemory(BinaryenRustModuleOptions *options, - bool import) { - options->importMemory = import; -} - -extern "C" BinaryenRustModule* -BinaryenRustModuleCreate(const BinaryenRustModuleOptions *options, - const char *assembly) { - Linker linker( - options->globalBase, - options->stackAllocation, - options->initialMem, - options->maxMem, - options->importMemory, - options->ignoreUnknownSymbols, - options->startFunction, - options->debug); - - S2WasmBuilder mainbuilder(assembly, options->debug); - linker.linkObject(mainbuilder); - linker.layout(); - - auto ret = make_unique(); - { - WasmBinaryWriter writer(&linker.getOutput().wasm, ret->buffer, options->debug); - writer.setNamesSection(options->debugInfo); - - std::unique_ptr sourceMapStream = nullptr; - { - sourceMapStream = make_unique(); - writer.setSourceMap(sourceMapStream.get(), options->sourceMapUrl); - } - - // FIXME: support symbol maps? - // writer.setSymbolMap(symbolMap); - writer.write(); - - if (sourceMapStream) { - ret->sourceMapJSON = sourceMapStream->str(); - } - } - return ret.release(); -} - -extern "C" const uint8_t* -BinaryenRustModulePtr(const BinaryenRustModule *M) { - return M->buffer.data(); -} - -extern "C" size_t -BinaryenRustModuleLen(const BinaryenRustModule *M) { - return M->buffer.size(); -} - -extern "C" const char* -BinaryenRustModuleSourceMapPtr(const BinaryenRustModule *M) { - return M->sourceMapJSON.data(); -} - -extern "C" size_t -BinaryenRustModuleSourceMapLen(const BinaryenRustModule *M) { - return M->sourceMapJSON.length(); -} - -extern "C" void -BinaryenRustModuleFree(BinaryenRustModule *M) { - delete M; -} diff --git a/src/librustc_binaryen/Cargo.toml b/src/librustc_binaryen/Cargo.toml deleted file mode 100644 index 9573c89471404..0000000000000 --- a/src/librustc_binaryen/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -# Wondering what this crate is? Take a look at the `lib.rs`! - -[package] -name = "rustc_binaryen" -version = "0.0.0" -authors = ["The Rust Project Developers"] - -[lib] -path = "lib.rs" - -[dependencies] -libc = "0.2" - -[build-dependencies] -cmake = "0.1" -cc = "1.0" diff --git a/src/librustc_binaryen/build.rs b/src/librustc_binaryen/build.rs deleted file mode 100644 index f23ff3cee555b..0000000000000 --- a/src/librustc_binaryen/build.rs +++ /dev/null @@ -1,60 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -extern crate cc; -extern crate cmake; - -use std::env; - -use cmake::Config; - -fn main() { - let target = env::var("TARGET").unwrap(); - - // Bring in `__emutls_get_address` which is apparently needed for now - if target.contains("pc-windows-gnu") { - println!("cargo:rustc-link-lib=gcc_eh"); - println!("cargo:rustc-link-lib=pthread"); - } - - Config::new("../binaryen") - .define("BUILD_STATIC_LIB", "ON") - .build_target("binaryen") - .build(); - - // I couldn't figure out how to link just one of these, so link everything. - println!("cargo:rustc-link-lib=static=asmjs"); - println!("cargo:rustc-link-lib=static=binaryen"); - println!("cargo:rustc-link-lib=static=cfg"); - println!("cargo:rustc-link-lib=static=emscripten-optimizer"); - println!("cargo:rustc-link-lib=static=ir"); - println!("cargo:rustc-link-lib=static=passes"); - println!("cargo:rustc-link-lib=static=support"); - println!("cargo:rustc-link-lib=static=wasm"); - - let out_dir = env::var("OUT_DIR").unwrap(); - println!("cargo:rustc-link-search=native={}/build/lib", out_dir); - - // Add in our own little shim along with some extra files that weren't - // included in the main build. - let mut cfg = cc::Build::new(); - cfg.file("BinaryenWrapper.cpp") - .file("../binaryen/src/wasm-linker.cpp") - .file("../binaryen/src/wasm-emscripten.cpp") - .include("../binaryen/src") - .cpp_link_stdlib(None) - .warnings(false) - .cpp(true); - - if !target.contains("msvc") { - cfg.flag("-std=c++11"); - } - cfg.compile("binaryen_wrapper"); -} diff --git a/src/librustc_binaryen/lib.rs b/src/librustc_binaryen/lib.rs deleted file mode 100644 index 36174e11ba04a..0000000000000 --- a/src/librustc_binaryen/lib.rs +++ /dev/null @@ -1,172 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Rustc bindings to the binaryen project. -//! -//! This crate is a small shim around the binaryen project which provides us the -//! ability to take LLVM's output and generate a wasm module. Specifically this -//! only supports one operation, creating a module from LLVM's assembly format -//! and then serializing that module to a wasm module. - -extern crate libc; - -use std::slice; -use std::ffi::{CString, CStr}; - -/// In-memory representation of a serialized wasm module. -pub struct Module { - ptr: *mut BinaryenRustModule, -} - -impl Module { - /// Creates a new wasm module from the LLVM-assembly provided (in a C string - /// format). - /// - /// The actual module creation can be tweaked through the various options in - /// `ModuleOptions` as well. Any errors are just returned as a bland string. - pub fn new(assembly: &CStr, opts: &ModuleOptions) -> Result { - unsafe { - let ptr = BinaryenRustModuleCreate(opts.ptr, assembly.as_ptr()); - if ptr.is_null() { - Err(format!("failed to create binaryen module")) - } else { - Ok(Module { ptr }) - } - } - } - - /// Returns the data of the serialized wasm module. This is a `foo.wasm` - /// file contents. - pub fn data(&self) -> &[u8] { - unsafe { - let ptr = BinaryenRustModulePtr(self.ptr); - let len = BinaryenRustModuleLen(self.ptr); - slice::from_raw_parts(ptr, len) - } - } - - /// Returns the data of the source map JSON. - pub fn source_map(&self) -> &[u8] { - unsafe { - let ptr = BinaryenRustModuleSourceMapPtr(self.ptr); - let len = BinaryenRustModuleSourceMapLen(self.ptr); - slice::from_raw_parts(ptr, len) - } - } -} - -impl Drop for Module { - fn drop(&mut self) { - unsafe { - BinaryenRustModuleFree(self.ptr); - } - } -} - -pub struct ModuleOptions { - ptr: *mut BinaryenRustModuleOptions, -} - -impl ModuleOptions { - pub fn new() -> ModuleOptions { - unsafe { - let ptr = BinaryenRustModuleOptionsCreate(); - ModuleOptions { ptr } - } - } - - /// Turns on or off debug info. - /// - /// From what I can tell this just creates a "names" section of the wasm - /// module which contains a table of the original function names. - pub fn debuginfo(&mut self, debug: bool) -> &mut Self { - unsafe { - BinaryenRustModuleOptionsSetDebugInfo(self.ptr, debug); - } - self - } - - /// Configures a `start` function for the module, to be executed when it's - /// loaded. - pub fn start(&mut self, func: &str) -> &mut Self { - let func = CString::new(func).unwrap(); - unsafe { - BinaryenRustModuleOptionsSetStart(self.ptr, func.as_ptr()); - } - self - } - - /// Configures a `sourceMappingURL` custom section value for the module. - pub fn source_map_url(&mut self, url: &str) -> &mut Self { - let url = CString::new(url).unwrap(); - unsafe { - BinaryenRustModuleOptionsSetSourceMapUrl(self.ptr, url.as_ptr()); - } - self - } - - /// Configures how much stack is initially allocated for the module. 1MB is - /// probably good enough for now. - pub fn stack(&mut self, amt: u64) -> &mut Self { - unsafe { - BinaryenRustModuleOptionsSetStackAllocation(self.ptr, amt); - } - self - } - - /// Flags whether the initial memory should be imported or exported. So far - /// we export it by default. - pub fn import_memory(&mut self, import: bool) -> &mut Self { - unsafe { - BinaryenRustModuleOptionsSetImportMemory(self.ptr, import); - } - self - } -} - -impl Drop for ModuleOptions { - fn drop(&mut self) { - unsafe { - BinaryenRustModuleOptionsFree(self.ptr); - } - } -} - -enum BinaryenRustModule {} -enum BinaryenRustModuleOptions {} - -extern { - fn BinaryenRustModuleCreate(opts: *const BinaryenRustModuleOptions, - assembly: *const libc::c_char) - -> *mut BinaryenRustModule; - fn BinaryenRustModulePtr(module: *const BinaryenRustModule) -> *const u8; - fn BinaryenRustModuleLen(module: *const BinaryenRustModule) -> usize; - fn BinaryenRustModuleSourceMapPtr(module: *const BinaryenRustModule) -> *const u8; - fn BinaryenRustModuleSourceMapLen(module: *const BinaryenRustModule) -> usize; - fn BinaryenRustModuleFree(module: *mut BinaryenRustModule); - - fn BinaryenRustModuleOptionsCreate() - -> *mut BinaryenRustModuleOptions; - fn BinaryenRustModuleOptionsSetDebugInfo(module: *mut BinaryenRustModuleOptions, - debuginfo: bool); - fn BinaryenRustModuleOptionsSetStart(module: *mut BinaryenRustModuleOptions, - start: *const libc::c_char); - fn BinaryenRustModuleOptionsSetSourceMapUrl(module: *mut BinaryenRustModuleOptions, - sourceMapUrl: *const libc::c_char); - fn BinaryenRustModuleOptionsSetStackAllocation( - module: *mut BinaryenRustModuleOptions, - stack: u64, - ); - fn BinaryenRustModuleOptionsSetImportMemory( - module: *mut BinaryenRustModuleOptions, - import: bool, - ); - fn BinaryenRustModuleOptionsFree(module: *mut BinaryenRustModuleOptions); -} diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index 500c4fdf4e8dd..4b9c93088a517 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -21,7 +21,6 @@ rustc-demangle = "0.1.4" rustc_allocator = { path = "../librustc_allocator" } rustc_apfloat = { path = "../librustc_apfloat" } rustc_back = { path = "../librustc_back" } -rustc_binaryen = { path = "../librustc_binaryen" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_trans/back/command.rs b/src/librustc_trans/back/command.rs index 3b765a493e0e7..ace32d250f976 100644 --- a/src/librustc_trans/back/command.rs +++ b/src/librustc_trans/back/command.rs @@ -17,6 +17,8 @@ use std::io; use std::mem; use std::process::{self, Output}; +use rustc_back::LldFlavor; + #[derive(Clone)] pub struct Command { program: Program, @@ -28,6 +30,7 @@ pub struct Command { enum Program { Normal(OsString), CmdBatScript(OsString), + Lld(OsString, LldFlavor) } impl Command { @@ -39,6 +42,10 @@ impl Command { Command::_new(Program::CmdBatScript(program.as_ref().to_owned())) } + pub fn lld>(program: P, flavor: LldFlavor) -> Command { + Command::_new(Program::Lld(program.as_ref().to_owned(), flavor)) + } + fn _new(program: Program) -> Command { Command { program, @@ -101,6 +108,13 @@ impl Command { c.arg("/c").arg(p); c } + Program::Lld(ref p, flavor) => { + let mut c = process::Command::new(p); + c.arg("-flavor").arg(match flavor { + LldFlavor::Wasm => "wasm", + }); + c + } }; ret.args(&self.args); ret.envs(self.env.clone()); diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index f050edcd513b9..d2f438af565b6 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -15,6 +15,7 @@ use super::command::Command; use super::rpath::RPathConfig; use super::rpath; use metadata::METADATA_FILENAME; +use rustc_back::LinkerFlavor; use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, PrintRequest}; use rustc::session::config::{RUST_CGU_EXT, Lto}; use rustc::session::filesearch; @@ -27,7 +28,7 @@ use rustc::util::common::time; use rustc::util::fs::fix_windows_verbatim_for_gcc; use rustc::hir::def_id::CrateNum; use tempdir::TempDir; -use rustc_back::{PanicStrategy, RelroLevel, LinkerFlavor}; +use rustc_back::{PanicStrategy, RelroLevel}; use context::get_reloc_model; use llvm; @@ -82,6 +83,10 @@ pub fn get_linker(sess: &Session) -> (PathBuf, Command, Vec<(OsString, OsString) } else if sess.target.target.options.is_like_msvc { let (cmd, envs) = msvc_link_exe_cmd(sess); (PathBuf::from("link.exe"), cmd, envs) + } else if let LinkerFlavor::Lld(f) = sess.linker_flavor() { + let linker = PathBuf::from(&sess.target.target.options.linker); + let cmd = Command::lld(&linker, f); + (linker, cmd, envs) } else { let linker = PathBuf::from(&sess.target.target.options.linker); let cmd = cmd(&linker); @@ -564,11 +569,6 @@ fn link_natively(sess: &Session, info!("preparing {:?} to {:?}", crate_type, out_filename); let flavor = sess.linker_flavor(); - // The "binaryen linker" is massively special, so skip everything below. - if flavor == LinkerFlavor::Binaryen { - return link_binaryen(sess, crate_type, out_filename, trans, tmpdir); - } - // The invocations of cc share some flags across platforms let (pname, mut cmd, envs) = get_linker(sess); // This will set PATH on windows @@ -1385,33 +1385,6 @@ fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { } } -/// For now "linking with binaryen" is just "move the one module we generated in -/// the backend to the final output" -/// -/// That is, all the heavy lifting happens during the `back::write` phase. Here -/// we just clean up after that. -/// -/// Note that this is super temporary and "will not survive the night", this is -/// guaranteed to get removed as soon as a linker for wasm exists. This should -/// not be used for anything other than wasm. -fn link_binaryen(sess: &Session, - _crate_type: config::CrateType, - out_filename: &Path, - trans: &CrateTranslation, - _tmpdir: &Path) { - assert!(trans.allocator_module.is_none()); - assert_eq!(trans.modules.len(), 1); - - let object = trans.modules[0].object.as_ref().expect("object must exist"); - let res = fs::hard_link(object, out_filename) - .or_else(|_| fs::copy(object, out_filename).map(|_| ())); - if let Err(e) = res { - sess.fatal(&format!("failed to create `{}`: {}", - out_filename.display(), - e)); - } -} - fn is_full_lto_enabled(sess: &Session) -> bool { match sess.lto() { Lto::Yes | diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index aa29c3cc12058..967b49a3f953b 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -23,7 +23,7 @@ use rustc::middle::dependency_format::Linkage; use rustc::session::Session; use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel}; use rustc::ty::TyCtxt; -use rustc_back::LinkerFlavor; +use rustc_back::{LinkerFlavor, LldFlavor}; use serialize::{json, Encoder}; /// For all the linkers we support, and information they might @@ -77,8 +77,11 @@ impl LinkerInfo { is_ld: true, }) as Box } - LinkerFlavor::Binaryen => { - panic!("can't instantiate binaryen linker") + + LinkerFlavor::Lld(LldFlavor::Wasm) => { + Box::new(WasmLd { + cmd, + }) as Box } } } @@ -775,3 +778,108 @@ fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec { symbols } + +pub struct WasmLd { + cmd: Command, +} + +impl Linker for WasmLd { + fn link_dylib(&mut self, lib: &str) { + self.cmd.arg("-l").arg(lib); + } + + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg("-l").arg(lib); + } + + fn link_rlib(&mut self, lib: &Path) { + self.cmd.arg(lib); + } + + fn include_path(&mut self, path: &Path) { + self.cmd.arg("-L").arg(path); + } + + fn framework_path(&mut self, _path: &Path) { + panic!("frameworks not supported") + } + + fn output_filename(&mut self, path: &Path) { + self.cmd.arg("-o").arg(path); + } + + fn add_object(&mut self, path: &Path) { + self.cmd.arg(path); + } + + fn position_independent_executable(&mut self) { + } + + fn partial_relro(&mut self) { + } + + fn full_relro(&mut self) { + } + + fn build_static_executable(&mut self) { + } + + fn args(&mut self, args: &[String]) { + self.cmd.args(args); + } + + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.cmd.arg("-l").arg(lib); + } + + fn link_framework(&mut self, _framework: &str) { + panic!("frameworks not supported") + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + self.cmd.arg("-l").arg(lib); + } + + fn link_whole_rlib(&mut self, lib: &Path) { + self.cmd.arg(lib); + } + + fn gc_sections(&mut self, _keep_metadata: bool) { + } + + fn optimize(&mut self) { + } + + fn debuginfo(&mut self) { + } + + fn no_default_libraries(&mut self) { + } + + fn build_dylib(&mut self, _out_filename: &Path) { + } + + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) { + } + + fn subsystem(&mut self, _subsystem: &str) { + } + + fn finalize(&mut self) -> Command { + self.cmd.arg("--threads"); + + // FIXME we probably shouldn't pass this but instead pass an explicit + // whitelist of symbols we'll allow to be undefined. Unfortunately + // though we can't handle symbols like `log10` that LLVM injects at a + // super late date without actually parsing object files. For now let's + // stick to this and hopefully fix it before stabilization happens. + self.cmd.arg("--allow-undefined"); + + // For now we just never have an entry symbol + self.cmd.arg("--no-entry"); + + let mut cmd = Command::new(""); + ::std::mem::swap(&mut cmd, &mut self.cmd); + cmd + } +} diff --git a/src/librustc_trans/back/symbol_export.rs b/src/librustc_trans/back/symbol_export.rs index 989ef8a953746..6aac6ceeca1d4 100644 --- a/src/librustc_trans/back/symbol_export.rs +++ b/src/librustc_trans/back/symbol_export.rs @@ -21,7 +21,6 @@ use rustc::ty::TyCtxt; use rustc::ty::maps::Providers; use rustc::util::nodemap::FxHashMap; use rustc_allocator::ALLOCATOR_METHODS; -use rustc_back::LinkerFlavor; use syntax::attr; pub type ExportedSymbols = FxHashMap< @@ -156,26 +155,12 @@ pub fn provide_extern(providers: &mut Providers) { let special_runtime_crate = tcx.is_panic_runtime(cnum) || tcx.is_compiler_builtins(cnum); - // Dealing with compiler-builtins and wasm right now is super janky. - // There's no linker! As a result we need all of the compiler-builtins - // exported symbols to make their way through all the way to the end of - // compilation. We want to make sure that LLVM doesn't remove them as - // well because we may or may not need them in the final output - // artifact. For now just force them to always get exported at the C - // layer, and we'll worry about gc'ing them later. - let compiler_builtins_and_binaryen = - tcx.is_compiler_builtins(cnum) && - tcx.sess.linker_flavor() == LinkerFlavor::Binaryen; - let mut crate_exports: Vec<_> = tcx .exported_symbol_ids(cnum) .iter() .map(|&def_id| { let name = tcx.symbol_name(Instance::mono(tcx, def_id)); - let export_level = if compiler_builtins_and_binaryen && - tcx.contains_extern_indicator(def_id) { - SymbolExportLevel::C - } else if special_runtime_crate { + let export_level = if special_runtime_crate { // We can probably do better here by just ensuring that // it has hidden visibility rather than public // visibility, as this is primarily here to ensure it's diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index ded9a296817b3..2fe02a381b754 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -23,7 +23,6 @@ use rustc::session::config::{self, OutputFilenames, OutputType, Passes, SomePass AllPasses, Sanitizer, Lto}; use rustc::session::Session; use rustc::util::nodemap::FxHashMap; -use rustc_back::LinkerFlavor; use time_graph::{self, TimeGraph, Timeline}; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; @@ -344,9 +343,7 @@ pub struct CodegenContext { pub tm_factory: Arc Result + Send + Sync>, pub msvc_imps_needed: bool, pub target_pointer_width: String, - binaryen_linker: bool, debuginfo: config::DebugInfoLevel, - wasm_import_memory: bool, // Number of cgus excluding the allocator/metadata modules pub total_cgus: usize, @@ -639,13 +636,6 @@ unsafe fn codegen(cgcx: &CodegenContext, f(cpm) } - // If we're going to generate wasm code from the assembly that llvm - // generates then we'll be transitively affecting a ton of options below. - // This only happens on the wasm target now. - let asm2wasm = cgcx.binaryen_linker && - !cgcx.crate_types.contains(&config::CrateTypeRlib) && - mtrans.kind == ModuleKind::Regular; - // If we don't have the integrated assembler, then we need to emit asm // from LLVM and use `gcc` to create the object file. let asm_to_obj = config.emit_obj && config.no_integrated_as; @@ -654,10 +644,10 @@ unsafe fn codegen(cgcx: &CodegenContext, // just llvm bitcode. In that case write bitcode, and possibly // delete the bitcode if it wasn't requested. Don't generate the // machine code, instead copy the .o file from the .bc - let write_bc = config.emit_bc || (config.obj_is_bitcode && !asm2wasm); - let rm_bc = !config.emit_bc && config.obj_is_bitcode && !asm2wasm; - let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm && !asm_to_obj; - let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode && !asm2wasm; + let write_bc = config.emit_bc || config.obj_is_bitcode; + let rm_bc = !config.emit_bc && config.obj_is_bitcode; + let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm_to_obj; + let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode; let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); @@ -736,13 +726,13 @@ unsafe fn codegen(cgcx: &CodegenContext, timeline.record("ir"); } - if config.emit_asm || (asm2wasm && config.emit_obj) || asm_to_obj { + if config.emit_asm || asm_to_obj { let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); // We can't use the same module for asm and binary output, because that triggers // various errors like invalid IR or broken binaries, so we might have to clone the // module to produce the asm output - let llmod = if config.emit_obj && !asm2wasm { + let llmod = if config.emit_obj { llvm::LLVMCloneModule(llmod) } else { llmod @@ -751,24 +741,13 @@ unsafe fn codegen(cgcx: &CodegenContext, write_output_file(diag_handler, tm, cpm, llmod, &path, llvm::FileType::AssemblyFile) })?; - if config.emit_obj && !asm2wasm { + if config.emit_obj { llvm::LLVMDisposeModule(llmod); } timeline.record("asm"); } - if asm2wasm && config.emit_obj { - let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); - let suffix = ".wasm.map"; // FIXME use target suffix - let map = cgcx.output_filenames.path(OutputType::Exe) - .with_extension(&suffix[1..]); - binaryen_assemble(cgcx, diag_handler, &assembly, &obj_out, &map); - timeline.record("binaryen"); - - if !config.emit_asm { - drop(fs::remove_file(&assembly)); - } - } else if write_obj { + if write_obj { with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file(diag_handler, tm, cpm, llmod, &obj_out, llvm::FileType::ObjectFile) @@ -808,49 +787,6 @@ unsafe fn codegen(cgcx: &CodegenContext, &cgcx.output_filenames)) } -/// Translates the LLVM-generated `assembly` on the filesystem into a wasm -/// module using binaryen, placing the output at `object`. -/// -/// In this case the "object" is actually a full and complete wasm module. We -/// won't actually be doing anything else to the output for now. This is all -/// pretty janky and will get removed as soon as a linker for wasm exists. -fn binaryen_assemble(cgcx: &CodegenContext, - handler: &Handler, - assembly: &Path, - object: &Path, - map: &Path) { - use rustc_binaryen::{Module, ModuleOptions}; - - let input = fs::read(&assembly).and_then(|contents| { - Ok(CString::new(contents)?) - }); - let mut options = ModuleOptions::new(); - if cgcx.debuginfo != config::NoDebugInfo { - options.debuginfo(true); - let map_file_name = map.file_name().unwrap(); - options.source_map_url(map_file_name.to_str().unwrap()); - } - - options.stack(1024 * 1024); - options.import_memory(cgcx.wasm_import_memory); - let assembled = input.and_then(|input| { - Module::new(&input, &options) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) - }); - let err = assembled.and_then(|binary| { - fs::write(&object, binary.data()).and_then(|()| { - if cgcx.debuginfo != config::NoDebugInfo { - fs::write(map, binary.source_map()) - } else { - Ok(()) - } - }) - }); - if let Err(e) = err { - handler.err(&format!("failed to run binaryen assembler: {}", e)); - } -} - pub(crate) struct CompiledModules { pub modules: Vec, pub metadata_module: CompiledModule, @@ -1420,9 +1356,6 @@ fn start_executing_work(tcx: TyCtxt, each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); })); - let wasm_import_memory = - attr::contains_name(&tcx.hir.krate().attrs, "wasm_import_memory"); - let assembler_cmd = if modules_config.no_integrated_as { // HACK: currently we use linker (gcc) as our assembler let (name, mut cmd, _) = get_linker(sess); @@ -1460,9 +1393,7 @@ fn start_executing_work(tcx: TyCtxt, total_cgus, msvc_imps_needed: msvc_imps_needed(tcx), target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), - binaryen_linker: tcx.sess.linker_flavor() == LinkerFlavor::Binaryen, debuginfo: tcx.sess.opts.debuginfo, - wasm_import_memory, assembler_cmd, }; diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 908d3790170ac..db13d2b4860b6 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -49,7 +49,6 @@ extern crate rustc_mir; extern crate rustc_allocator; extern crate rustc_apfloat; extern crate rustc_back; -extern crate rustc_binaryen; extern crate rustc_const_math; extern crate rustc_data_structures; extern crate rustc_demangle; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ae0556320b0ef..f455b1034ff80 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -414,9 +414,6 @@ declare_features! ( // Allow trait methods with arbitrary self types (active, arbitrary_self_types, "1.23.0", Some(44874)), - // #![wasm_import_memory] attribute - (active, wasm_import_memory, "1.22.0", None), - // `crate` in paths (active, crate_in_paths, "1.23.0", Some(45477)), @@ -979,11 +976,6 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG never be stable", cfg_fn!(rustc_attrs))), - ("wasm_import_memory", Whitelisted, Gated(Stability::Unstable, - "wasm_import_memory", - "wasm_import_memory attribute is currently unstable", - cfg_fn!(wasm_import_memory))), - ("rustc_args_required_const", Whitelisted, Gated(Stability::Unstable, "rustc_attrs", "never will be stable", diff --git a/src/test/run-pass/issue-15487.rs b/src/test/run-pass/issue-15487.rs index cc5db0af88bea..3616ab9e6c730 100644 --- a/src/test/run-pass/issue-15487.rs +++ b/src/test/run-pass/issue-15487.rs @@ -9,6 +9,7 @@ // except according to those terms. // ignore-windows +// ignore-wasm32-bare no libs to link #![feature(link_args)] diff --git a/src/test/ui/feature-gate-wasm_import_memory.rs b/src/test/ui/feature-gate-wasm_import_memory.rs deleted file mode 100644 index a010ebb3551d0..0000000000000 --- a/src/test/ui/feature-gate-wasm_import_memory.rs +++ /dev/null @@ -1,14 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![wasm_import_memory] //~ ERROR: currently unstable - -fn main() {} - diff --git a/src/test/ui/feature-gate-wasm_import_memory.stderr b/src/test/ui/feature-gate-wasm_import_memory.stderr deleted file mode 100644 index 10190ef93f0d9..0000000000000 --- a/src/test/ui/feature-gate-wasm_import_memory.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error[E0658]: wasm_import_memory attribute is currently unstable - --> $DIR/feature-gate-wasm_import_memory.rs:11:1 - | -11 | #![wasm_import_memory] //~ ERROR: currently unstable - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(wasm_import_memory)] to the crate attributes to enable - -error: aborting due to previous error - diff --git a/src/tools/lld b/src/tools/lld new file mode 160000 index 0000000000000..2e1d6c2acb200 --- /dev/null +++ b/src/tools/lld @@ -0,0 +1 @@ +Subproject commit 2e1d6c2acb200abd359d2a94a86f7f0fe094068c diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 4d89008d5ca54..615e624fb86bc 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -50,7 +50,6 @@ pub mod unstable_book; fn filter_dirs(path: &Path) -> bool { let skip = [ - "src/binaryen", "src/dlmalloc", "src/jemalloc", "src/llvm", @@ -68,6 +67,7 @@ fn filter_dirs(path: &Path) -> bool { "src/tools/rust-installer", "src/tools/rustfmt", "src/tools/miri", + "src/tools/lld", "src/librustc/mir/interpret", "src/librustc_mir/interpret", "src/target", From ce52e0e7e09ec215926d4e93a767ea5fad4c70bf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 Feb 2018 12:09:25 -0800 Subject: [PATCH 03/42] rustc: Tweak default linker selection This commit refactors how the path to the linker that we're going to invoke is selected. Previously all targets listed *both* a `LinkerFlavor` and a `linker` (path) option, but this meant that whenever you changed one you had to change the other. The purpose of this commit is to avoid coupling these where possible. Target specifications now only unconditionally define the *flavor* of the linker that they're using by default. If not otherwise specified each flavor now implies a particular default linker to run. As a result, this means that if you'd like to test out `ld` for example you should be able to do: rustc -Z linker-flavor=ld foo.rs whereas previously you had to do rustc -Z linker-flavor=ld -C linker=ld foo.rs This will hopefully make it a bit easier to tinker around with variants that should otherwise be well known to work, for example with LLD, `ld` on OSX, etc. --- src/librustc_back/lib.rs | 6 ++ .../target/aarch64_unknown_cloudabi.rs | 2 +- .../target/armv7_unknown_cloudabi_eabihf.rs | 2 +- .../target/asmjs_unknown_emscripten.rs | 3 - src/librustc_back/target/emscripten_base.rs | 17 ----- src/librustc_back/target/haiku_base.rs | 1 - .../target/i686_unknown_cloudabi.rs | 2 +- src/librustc_back/target/l4re_base.rs | 1 - src/librustc_back/target/mod.rs | 9 ++- src/librustc_back/target/msp430_none_elf.rs | 2 +- src/librustc_back/target/thumb_base.rs | 2 +- .../target/wasm32_experimental_emscripten.rs | 3 - .../target/wasm32_unknown_emscripten.rs | 3 - .../target/wasm32_unknown_unknown.rs | 2 - src/librustc_back/target/windows_base.rs | 1 - src/librustc_back/target/windows_msvc_base.rs | 1 - .../target/x86_64_rumprun_netbsd.rs | 2 +- .../target/x86_64_unknown_cloudabi.rs | 2 +- src/librustc_trans/Cargo.toml | 4 +- src/librustc_trans/back/command.rs | 14 +--- src/librustc_trans/back/link.rs | 71 ++++++++----------- src/librustc_trans/back/linker.rs | 4 ++ src/librustc_trans/back/write.rs | 2 +- src/librustc_trans/lib.rs | 1 - 24 files changed, 56 insertions(+), 101 deletions(-) delete mode 100644 src/librustc_back/target/emscripten_base.rs diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 141c8954a3306..9de56cca3394f 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -57,6 +57,9 @@ pub enum LinkerFlavor { RustcEncodable, RustcDecodable)] pub enum LldFlavor { Wasm, + Ld64, + Ld, + Link, } impl ToJson for LinkerFlavor { @@ -94,6 +97,9 @@ flavor_mappings! { ((LinkerFlavor::Ld), "ld"), ((LinkerFlavor::Msvc), "msvc"), ((LinkerFlavor::Lld(LldFlavor::Wasm)), "wasm-ld"), + ((LinkerFlavor::Lld(LldFlavor::Ld64)), "ld64.lld"), + ((LinkerFlavor::Lld(LldFlavor::Ld)), "ld.lld"), + ((LinkerFlavor::Lld(LldFlavor::Link)), "lld-link"), } #[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] diff --git a/src/librustc_back/target/aarch64_unknown_cloudabi.rs b/src/librustc_back/target/aarch64_unknown_cloudabi.rs index 59c82e06a679c..a5d0e5bf166dd 100644 --- a/src/librustc_back/target/aarch64_unknown_cloudabi.rs +++ b/src/librustc_back/target/aarch64_unknown_cloudabi.rs @@ -15,7 +15,7 @@ pub fn target() -> TargetResult { let mut base = super::cloudabi_base::opts(); base.max_atomic_width = Some(128); base.abi_blacklist = super::arm_base::abi_blacklist(); - base.linker = "aarch64-unknown-cloudabi-cc".to_string(); + base.linker = Some("aarch64-unknown-cloudabi-cc".to_string()); Ok(Target { llvm_target: "aarch64-unknown-cloudabi".to_string(), diff --git a/src/librustc_back/target/armv7_unknown_cloudabi_eabihf.rs b/src/librustc_back/target/armv7_unknown_cloudabi_eabihf.rs index faa2c4fdceb9b..fa66a35abbf35 100644 --- a/src/librustc_back/target/armv7_unknown_cloudabi_eabihf.rs +++ b/src/librustc_back/target/armv7_unknown_cloudabi_eabihf.rs @@ -17,7 +17,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(64); base.features = "+v7,+vfp3,+neon".to_string(); base.abi_blacklist = super::arm_base::abi_blacklist(); - base.linker = "armv7-unknown-cloudabi-eabihf-cc".to_string(); + base.linker = Some("armv7-unknown-cloudabi-eabihf-cc".to_string()); Ok(Target { llvm_target: "armv7-unknown-cloudabi-eabihf".to_string(), diff --git a/src/librustc_back/target/asmjs_unknown_emscripten.rs b/src/librustc_back/target/asmjs_unknown_emscripten.rs index 5d9f0f6012bf2..f114926740a5e 100644 --- a/src/librustc_back/target/asmjs_unknown_emscripten.rs +++ b/src/librustc_back/target/asmjs_unknown_emscripten.rs @@ -10,7 +10,6 @@ use LinkerFlavor; use super::{LinkArgs, Target, TargetOptions}; -use super::emscripten_base::{cmd}; pub fn target() -> Result { let mut args = LinkArgs::new(); @@ -19,8 +18,6 @@ pub fn target() -> Result { "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string()]); let opts = TargetOptions { - linker: cmd("emcc"), - dynamic_linking: false, executables: true, exe_suffix: ".js".to_string(), diff --git a/src/librustc_back/target/emscripten_base.rs b/src/librustc_back/target/emscripten_base.rs deleted file mode 100644 index bacada3f5ab02..0000000000000 --- a/src/librustc_back/target/emscripten_base.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn cmd(name: &str) -> String { - if cfg!(windows) { - format!("{}.bat", name) - } else { - name.to_string() - } -} diff --git a/src/librustc_back/target/haiku_base.rs b/src/librustc_back/target/haiku_base.rs index 112f424f7a8bb..a1ccb632cab79 100644 --- a/src/librustc_back/target/haiku_base.rs +++ b/src/librustc_back/target/haiku_base.rs @@ -13,7 +13,6 @@ use std::default::Default; pub fn opts() -> TargetOptions { TargetOptions { - linker: "cc".to_string(), dynamic_linking: true, executables: true, has_rpath: false, diff --git a/src/librustc_back/target/i686_unknown_cloudabi.rs b/src/librustc_back/target/i686_unknown_cloudabi.rs index e244f443d3e3a..69c3b298caba0 100644 --- a/src/librustc_back/target/i686_unknown_cloudabi.rs +++ b/src/librustc_back/target/i686_unknown_cloudabi.rs @@ -15,7 +15,7 @@ pub fn target() -> TargetResult { let mut base = super::cloudabi_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); - base.linker = "i686-unknown-cloudabi-cc".to_string(); + base.linker = Some("i686-unknown-cloudabi-cc".to_string()); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.stack_probes = true; diff --git a/src/librustc_back/target/l4re_base.rs b/src/librustc_back/target/l4re_base.rs index 31d428d266839..7cb7f8d613dee 100644 --- a/src/librustc_back/target/l4re_base.rs +++ b/src/librustc_back/target/l4re_base.rs @@ -73,7 +73,6 @@ pub fn opts() -> Result { has_elf_tls: false, exe_allocation_crate: None, panic_strategy: PanicStrategy::Abort, - linker: "ld".to_string(), pre_link_args, post_link_args, target_family: Some("unix".to_string()), diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 2872c59157d6b..c3a6fe6feb4b8 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -58,7 +58,6 @@ mod arm_base; mod bitrig_base; mod cloudabi_base; mod dragonfly_base; -mod emscripten_base; mod freebsd_base; mod haiku_base; mod linux_base; @@ -276,8 +275,8 @@ pub struct TargetOptions { /// Whether the target is built-in or loaded from a custom target specification. pub is_builtin: bool, - /// Linker to invoke. Defaults to "cc". - pub linker: String, + /// Linker to invoke + pub linker: Option, /// Linker arguments that are unconditionally passed *before* any /// user-defined libraries. @@ -480,7 +479,7 @@ impl Default for TargetOptions { fn default() -> TargetOptions { TargetOptions { is_builtin: false, - linker: option_env!("CFG_DEFAULT_LINKER").unwrap_or("cc").to_string(), + linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()), pre_link_args: LinkArgs::new(), post_link_args: LinkArgs::new(), asm_args: Vec::new(), @@ -730,7 +729,7 @@ impl Target { } key!(is_builtin, bool); - key!(linker); + key!(linker, optional); key!(pre_link_args, link_args); key!(pre_link_objects_exe, list); key!(pre_link_objects_dll, list); diff --git a/src/librustc_back/target/msp430_none_elf.rs b/src/librustc_back/target/msp430_none_elf.rs index 966df897f01f1..d0f512ae47cd9 100644 --- a/src/librustc_back/target/msp430_none_elf.rs +++ b/src/librustc_back/target/msp430_none_elf.rs @@ -32,7 +32,7 @@ pub fn target() -> TargetResult { // to gcc to get object files. For this reason we have a hard // dependency on this specific gcc. asm_args: vec!["-mcpu=msp430".to_string()], - linker: "msp430-elf-gcc".to_string(), + linker: Some("msp430-elf-gcc".to_string()), no_integrated_as: true, // There are no atomic instructions available in the MSP430 diff --git a/src/librustc_back/target/thumb_base.rs b/src/librustc_back/target/thumb_base.rs index 6bb496649a858..6a8f52f509310 100644 --- a/src/librustc_back/target/thumb_base.rs +++ b/src/librustc_back/target/thumb_base.rs @@ -45,7 +45,7 @@ pub fn opts() -> TargetOptions { executables: true, // In 99%+ of cases, we want to use the `arm-none-eabi-gcc` compiler (there aren't many // options around) - linker: "arm-none-eabi-gcc".to_string(), + linker: Some("arm-none-eabi-gcc".to_string()), // Because these devices have very little resources having an unwinder is too onerous so we // default to "abort" because the "unwind" strategy is very rare. panic_strategy: PanicStrategy::Abort, diff --git a/src/librustc_back/target/wasm32_experimental_emscripten.rs b/src/librustc_back/target/wasm32_experimental_emscripten.rs index a261c982b3f24..13dee3a576869 100644 --- a/src/librustc_back/target/wasm32_experimental_emscripten.rs +++ b/src/librustc_back/target/wasm32_experimental_emscripten.rs @@ -10,7 +10,6 @@ use LinkerFlavor; use super::{LinkArgs, Target, TargetOptions}; -use super::emscripten_base::{cmd}; pub fn target() -> Result { let mut post_link_args = LinkArgs::new(); @@ -24,8 +23,6 @@ pub fn target() -> Result { "-g3".to_string()]); let opts = TargetOptions { - linker: cmd("emcc"), - dynamic_linking: false, executables: true, // Today emcc emits two files - a .js file to bootstrap and diff --git a/src/librustc_back/target/wasm32_unknown_emscripten.rs b/src/librustc_back/target/wasm32_unknown_emscripten.rs index 4823541f2262c..2770e67e30a5b 100644 --- a/src/librustc_back/target/wasm32_unknown_emscripten.rs +++ b/src/librustc_back/target/wasm32_unknown_emscripten.rs @@ -10,7 +10,6 @@ use LinkerFlavor; use super::{LinkArgs, Target, TargetOptions}; -use super::emscripten_base::{cmd}; pub fn target() -> Result { let mut post_link_args = LinkArgs::new(); @@ -21,8 +20,6 @@ pub fn target() -> Result { "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string()]); let opts = TargetOptions { - linker: cmd("emcc"), - dynamic_linking: false, executables: true, // Today emcc emits two files - a .js file to bootstrap and diff --git a/src/librustc_back/target/wasm32_unknown_unknown.rs b/src/librustc_back/target/wasm32_unknown_unknown.rs index c771762cfc89b..1d84e13751740 100644 --- a/src/librustc_back/target/wasm32_unknown_unknown.rs +++ b/src/librustc_back/target/wasm32_unknown_unknown.rs @@ -22,8 +22,6 @@ use super::{Target, TargetOptions, PanicStrategy}; pub fn target() -> Result { let opts = TargetOptions { - linker: "lld".to_string(), - // we allow dynamic linking, but only cdylibs. Basically we allow a // final library artifact that exports some symbols (a wasm module) but // we don't allow intermediate `dylib` crate types diff --git a/src/librustc_back/target/windows_base.rs b/src/librustc_back/target/windows_base.rs index cc40b8b052983..b3eb65ecdce37 100644 --- a/src/librustc_back/target/windows_base.rs +++ b/src/librustc_back/target/windows_base.rs @@ -75,7 +75,6 @@ pub fn opts() -> TargetOptions { TargetOptions { // FIXME(#13846) this should be enabled for windows function_sections: false, - linker: "gcc".to_string(), dynamic_linking: true, executables: true, dll_prefix: "".to_string(), diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs index 64df6624dd1c2..b57c9e62f484a 100644 --- a/src/librustc_back/target/windows_msvc_base.rs +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -20,7 +20,6 @@ pub fn opts() -> TargetOptions { TargetOptions { function_sections: true, - linker: "link.exe".to_string(), dynamic_linking: true, executables: true, dll_prefix: "".to_string(), diff --git a/src/librustc_back/target/x86_64_rumprun_netbsd.rs b/src/librustc_back/target/x86_64_rumprun_netbsd.rs index 18f6380b6eedf..3158665a2e28d 100644 --- a/src/librustc_back/target/x86_64_rumprun_netbsd.rs +++ b/src/librustc_back/target/x86_64_rumprun_netbsd.rs @@ -15,7 +15,7 @@ pub fn target() -> TargetResult { let mut base = super::netbsd_base::opts(); base.cpu = "x86-64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.linker = "x86_64-rumprun-netbsd-gcc".to_string(); + base.linker = Some("x86_64-rumprun-netbsd-gcc".to_string()); base.max_atomic_width = Some(64); base.dynamic_linking = false; diff --git a/src/librustc_back/target/x86_64_unknown_cloudabi.rs b/src/librustc_back/target/x86_64_unknown_cloudabi.rs index 1ce3c6444f1ea..d1a9cb1cd7e7d 100644 --- a/src/librustc_back/target/x86_64_unknown_cloudabi.rs +++ b/src/librustc_back/target/x86_64_unknown_cloudabi.rs @@ -15,7 +15,7 @@ pub fn target() -> TargetResult { let mut base = super::cloudabi_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.linker = "x86_64-unknown-cloudabi-cc".to_string(); + base.linker = Some("x86_64-unknown-cloudabi-cc".to_string()); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index 4b9c93088a517..805d2086ee4f6 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -11,6 +11,7 @@ test = false [dependencies] bitflags = "1.0" +cc = "1.0.1" flate2 = "1.0" jobserver = "0.1.5" libc = "0.2" @@ -34,9 +35,6 @@ syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } tempdir = "0.3" -[target."cfg(windows)".dependencies] -cc = "1.0.1" - [features] # Used to communicate the feature to `rustc_back` in the same manner that the # `rustc` driver script communicate this. diff --git a/src/librustc_trans/back/command.rs b/src/librustc_trans/back/command.rs index ace32d250f976..9dc55e90d946f 100644 --- a/src/librustc_trans/back/command.rs +++ b/src/librustc_trans/back/command.rs @@ -81,17 +81,6 @@ impl Command { self } - pub fn envs(&mut self, envs: I) -> &mut Command - where I: IntoIterator, - K: AsRef, - V: AsRef - { - for (key, value) in envs { - self._env(key.as_ref(), value.as_ref()); - } - self - } - fn _env(&mut self, key: &OsStr, value: &OsStr) { self.env.push((key.to_owned(), value.to_owned())); } @@ -112,6 +101,9 @@ impl Command { let mut c = process::Command::new(p); c.arg("-flavor").arg(match flavor { LldFlavor::Wasm => "wasm", + LldFlavor::Ld => "gnu", + LldFlavor::Link => "link", + LldFlavor::Ld64 => "darwin", }); c } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index d2f438af565b6..f5b06f492e7f4 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use cc::windows_registry; use super::archive::{ArchiveBuilder, ArchiveConfig}; use super::bytecode::RLIB_BYTECODE_EXTENSION; use super::linker::Linker; @@ -58,9 +59,7 @@ pub use rustc_trans_utils::link::{find_crate_name, filename_for_input, default_o // The third parameter is for env vars, used on windows to set up the // path for MSVC to find its DLLs, and gcc to find its bundled // toolchain -pub fn get_linker(sess: &Session) -> (PathBuf, Command, Vec<(OsString, OsString)>) { - let envs = vec![("PATH".into(), command_path(sess))]; - +pub fn get_linker(sess: &Session) -> (PathBuf, Command) { // If our linker looks like a batch script on Windows then to execute this // we'll need to spawn `cmd` explicitly. This is primarily done to handle // emscripten where the linker is `emcc.bat` and needs to be spawned as @@ -75,49 +74,41 @@ pub fn get_linker(sess: &Session) -> (PathBuf, Command, Vec<(OsString, OsString) return Command::bat_script(linker) } } - Command::new(linker) + match sess.linker_flavor() { + LinkerFlavor::Lld(f) => Command::lld(linker, f), + _ => Command::new(linker), + + } }; - if let Some(ref linker) = sess.opts.cg.linker { - (linker.clone(), cmd(linker), envs) - } else if sess.target.target.options.is_like_msvc { - let (cmd, envs) = msvc_link_exe_cmd(sess); - (PathBuf::from("link.exe"), cmd, envs) - } else if let LinkerFlavor::Lld(f) = sess.linker_flavor() { - let linker = PathBuf::from(&sess.target.target.options.linker); - let cmd = Command::lld(&linker, f); - (linker, cmd, envs) - } else { - let linker = PathBuf::from(&sess.target.target.options.linker); - let cmd = cmd(&linker); - (linker, cmd, envs) - } -} + let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple, "link.exe"); -#[cfg(windows)] -pub fn msvc_link_exe_cmd(sess: &Session) -> (Command, Vec<(OsString, OsString)>) { - use cc::windows_registry; + let linker_path = sess.opts.cg.linker.as_ref().map(|s| &**s) + .or(sess.target.target.options.linker.as_ref().map(|s| s.as_ref())) + .unwrap_or(match sess.linker_flavor() { + LinkerFlavor::Msvc => { + msvc_tool.as_ref().map(|t| t.path()).unwrap_or("link.exe".as_ref()) + } + LinkerFlavor::Em if cfg!(windows) => "emcc.bat".as_ref(), + LinkerFlavor::Em => "emcc".as_ref(), + LinkerFlavor::Gcc => "gcc".as_ref(), + LinkerFlavor::Ld => "ld".as_ref(), + LinkerFlavor::Lld(_) => "lld".as_ref(), + }); - let target = &sess.opts.target_triple; - let tool = windows_registry::find_tool(target, "link.exe"); + let mut cmd = cmd(linker_path); - if let Some(tool) = tool { - let mut cmd = Command::new(tool.path()); - cmd.args(tool.args()); - for &(ref k, ref v) in tool.env() { - cmd.env(k, v); + if sess.target.target.options.is_like_msvc { + if let Some(ref tool) = msvc_tool { + cmd.args(tool.args()); + for &(ref k, ref v) in tool.env() { + cmd.env(k, v); + } } - let envs = tool.env().to_vec(); - (cmd, envs) - } else { - debug!("Failed to locate linker."); - (Command::new("link.exe"), vec![]) } -} + cmd.env("PATH", command_path(sess)); -#[cfg(not(windows))] -pub fn msvc_link_exe_cmd(_sess: &Session) -> (Command, Vec<(OsString, OsString)>) { - (Command::new("link.exe"), vec![]) + (linker_path.to_path_buf(), cmd) } fn command_path(sess: &Session) -> OsString { @@ -570,9 +561,7 @@ fn link_natively(sess: &Session, let flavor = sess.linker_flavor(); // The invocations of cc share some flags across platforms - let (pname, mut cmd, envs) = get_linker(sess); - // This will set PATH on windows - cmd.envs(envs); + let (pname, mut cmd) = get_linker(sess); let root = sess.target_filesearch(PathKind::Native).get_lib_path(); if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 967b49a3f953b..186c3298bfa2a 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -45,6 +45,7 @@ impl LinkerInfo { cmd: Command, sess: &'a Session) -> Box { match sess.linker_flavor() { + LinkerFlavor::Lld(LldFlavor::Link) | LinkerFlavor::Msvc => { Box::new(MsvcLinker { cmd, @@ -68,6 +69,9 @@ impl LinkerInfo { is_ld: false, }) as Box } + + LinkerFlavor::Lld(LldFlavor::Ld) | + LinkerFlavor::Lld(LldFlavor::Ld64) | LinkerFlavor::Ld => { Box::new(GccLinker { cmd, diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 2fe02a381b754..48fcf54dd8c4b 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -1358,7 +1358,7 @@ fn start_executing_work(tcx: TyCtxt, let assembler_cmd = if modules_config.no_integrated_as { // HACK: currently we use linker (gcc) as our assembler - let (name, mut cmd, _) = get_linker(sess); + let (name, mut cmd) = get_linker(sess); cmd.args(&sess.target.target.options.asm_args); Some(Arc::new(AssemblerCommand { name, diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index db13d2b4860b6..5b3fad2ff7475 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -62,7 +62,6 @@ extern crate rustc_trans_utils; extern crate syntax_pos; extern crate rustc_errors as errors; extern crate serialize; -#[cfg(windows)] extern crate cc; // Used to locate MSVC extern crate tempdir; From a003cb7cd7d619a5553c5c99e4ee7ce185a1608c Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 8 Feb 2018 16:26:33 -0800 Subject: [PATCH 04/42] Add test. --- src/test/run-pass/hygiene/issue-47312.rs | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/test/run-pass/hygiene/issue-47312.rs diff --git a/src/test/run-pass/hygiene/issue-47312.rs b/src/test/run-pass/hygiene/issue-47312.rs new file mode 100644 index 0000000000000..5e83f3808d8cc --- /dev/null +++ b/src/test/run-pass/hygiene/issue-47312.rs @@ -0,0 +1,30 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] +#![allow(unused)] + +mod foo { + pub macro m($s:tt, $i:tt) { + $s.$i + } +} + +mod bar { + struct S(i32); + fn f() { + let s = S(0); + ::foo::m!(s, 0); + } +} + +fn main() {} From 0bb818cc0b2885b01ce670ce192aac1dbc6db16a Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 12 Feb 2018 01:39:01 -0800 Subject: [PATCH 05/42] Add Iterator::try_for_each The fallible version of for_each and the stateless version of try_fold. --- src/libcore/iter/iterator.rs | 48 +++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 296fb8733ba6c..a485e2c82bada 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -1366,9 +1366,9 @@ pub trait Iterator { /// /// In particular, try to have this call `try_fold()` on the internal parts /// from which this iterator is composed. If multiple calls are needed, - /// the `?` operator be convenient for chaining the accumulator value along, - /// but beware any invariants that need to be upheld before those early - /// returns. This is a `&mut self` method, so iteration needs to be + /// the `?` operator may be convenient for chaining the accumulator value + /// along, but beware any invariants that need to be upheld before those + /// early returns. This is a `&mut self` method, so iteration needs to be /// resumable after hitting an error here. /// /// # Examples @@ -1414,6 +1414,42 @@ pub trait Iterator { Try::from_ok(accum) } + /// An iterator method that applies a fallible function to each item in the + /// iterator, stopping at the first error and returning that error. + /// + /// This can also be thought of as the fallible form of [`for_each()`] + /// or as the stateless version of [`try_fold()`]. + /// + /// [`for_each()`]: #method.for_each + /// [`try_fold()`]: #method.try_fold + /// + /// # Examples + /// + /// ``` + /// #![feature(iterator_try_fold)] + /// use std::fs::rename; + /// use std::io::{stdout, Write}; + /// use std::path::Path; + /// + /// let data = ["no_tea.txt", "stale_bread.json", "torrential_rain.png"]; + /// + /// let res = data.iter().try_for_each(|x| writeln!(stdout(), "{}", x)); + /// assert!(res.is_ok()); + /// + /// let mut it = data.iter().cloned(); + /// let res = it.try_for_each(|x| rename(x, Path::new(x).with_extension("old"))); + /// assert!(res.is_err()); + /// // It short-circuited, so the remaining items are still in the iterator: + /// assert_eq!(it.next(), Some("stale_bread.json")); + /// ``` + #[inline] + #[unstable(feature = "iterator_try_fold", issue = "45594")] + fn try_for_each(&mut self, mut f: F) -> R where + Self: Sized, F: FnMut(Self::Item) -> R, R: Try + { + self.try_fold((), move |(), x| f(x)) + } + /// An iterator method that applies a function, producing a single, final value. /// /// `fold()` takes two arguments: an initial value, and a closure with two @@ -1528,7 +1564,7 @@ pub trait Iterator { fn all(&mut self, mut f: F) -> bool where Self: Sized, F: FnMut(Self::Item) -> bool { - self.try_fold((), move |(), x| { + self.try_for_each(move |x| { if f(x) { LoopState::Continue(()) } else { LoopState::Break(()) } }) == LoopState::Continue(()) @@ -1577,7 +1613,7 @@ pub trait Iterator { Self: Sized, F: FnMut(Self::Item) -> bool { - self.try_fold((), move |(), x| { + self.try_for_each(move |x| { if f(x) { LoopState::Break(()) } else { LoopState::Continue(()) } }) == LoopState::Break(()) @@ -1631,7 +1667,7 @@ pub trait Iterator { Self: Sized, P: FnMut(&Self::Item) -> bool, { - self.try_fold((), move |(), x| { + self.try_for_each(move |x| { if predicate(&x) { LoopState::Break(x) } else { LoopState::Continue(()) } }).break_value() From bd10ef7b27e7c6ab6e4e68898aa6ccd240fc57f3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 10 Feb 2018 09:54:27 -0500 Subject: [PATCH 06/42] rustc_typeck/check/closure: rustfmt --- src/librustc_typeck/check/closure.rs | 51 +++++++++++++++------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index df15f781ae8c9..5e9c283a0c6e8 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -42,8 +42,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ) -> Ty<'tcx> { debug!( "check_expr_closure(expr={:?},expected={:?})", - expr, - expected + expr, expected ); // It's always helpful for inference if we know the kind of @@ -68,8 +67,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ) -> Ty<'tcx> { debug!( "check_closure(opt_kind={:?}, expected_sig={:?})", - opt_kind, - expected_sig + opt_kind, expected_sig ); let expr_def_id = self.tcx.hir.local_def_id(expr.id); @@ -109,19 +107,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let closure_type = self.tcx.mk_closure(expr_def_id, substs); if let Some(GeneratorTypes { yield_ty, interior }) = generator_types { - self.demand_eqtype(expr.span, - yield_ty, - substs.generator_yield_ty(expr_def_id, self.tcx)); - self.demand_eqtype(expr.span, - liberated_sig.output(), - substs.generator_return_ty(expr_def_id, self.tcx)); + self.demand_eqtype( + expr.span, + yield_ty, + substs.generator_yield_ty(expr_def_id, self.tcx), + ); + self.demand_eqtype( + expr.span, + liberated_sig.output(), + substs.generator_return_ty(expr_def_id, self.tcx), + ); return self.tcx.mk_generator(expr_def_id, substs, interior); } debug!( "check_closure: expr.id={:?} closure_type={:?}", - expr.id, - closure_type + expr.id, closure_type ); // Tuple up the arguments and insert the resulting function type into @@ -138,20 +139,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { debug!( "check_closure: expr_def_id={:?}, sig={:?}, opt_kind={:?}", - expr_def_id, - sig, - opt_kind + expr_def_id, sig, opt_kind ); let sig_fn_ptr_ty = self.tcx.mk_fn_ptr(sig); - self.demand_eqtype(expr.span, - sig_fn_ptr_ty, - substs.closure_sig_ty(expr_def_id, self.tcx)); + self.demand_eqtype( + expr.span, + sig_fn_ptr_ty, + substs.closure_sig_ty(expr_def_id, self.tcx), + ); if let Some(kind) = opt_kind { - self.demand_eqtype(expr.span, - kind.to_ty(self.tcx), - substs.closure_kind_ty(expr_def_id, self.tcx)); + self.demand_eqtype( + expr.span, + kind.to_ty(self.tcx), + substs.closure_kind_ty(expr_def_id, self.tcx), + ); } closure_type @@ -314,8 +317,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let self_ty = self.shallow_resolve(trait_ref.self_ty()); debug!( "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})", - trait_ref, - self_ty + trait_ref, self_ty ); match self_ty.sty { ty::TyInfer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref), @@ -564,7 +566,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { body: &hir::Body, bound_sig: ty::PolyFnSig<'tcx>, ) -> ClosureSignatures<'tcx> { - let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, &bound_sig); + let liberated_sig = self.tcx() + .liberate_late_bound_regions(expr_def_id, &bound_sig); let liberated_sig = self.inh.normalize_associated_types_in( body.value.span, body.value.id, From cc05561048f07ae5e99b25bbe5db04eebe0c7cd2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 12 Feb 2018 16:00:15 -0500 Subject: [PATCH 07/42] detect wrong number of args when type-checking a closure Instead of creating inference variables for those argument types, use the trait error-reporting code to give a nicer error. --- src/librustc/traits/error_reporting.rs | 42 +++++- src/librustc/traits/mod.rs | 2 +- src/librustc_typeck/check/closure.rs | 131 +++++++++++++++--- ...ure-arg-count-expected-type-issue-47244.rs | 29 ++++ ...arg-count-expected-type-issue-47244.stderr | 14 ++ 5 files changed, 191 insertions(+), 27 deletions(-) create mode 100644 src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.rs create mode 100644 src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index a290839425ebe..f3aab4020594b 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -747,7 +747,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ty::TyTuple(ref tys, _) => tys.iter() .map(|t| match t.sty { ty::TypeVariants::TyTuple(ref tys, _) => ArgKind::Tuple( - span, + Some(span), tys.iter() .map(|ty| ("_".to_owned(), format!("{}", ty.sty))) .collect::>() @@ -815,7 +815,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } - fn get_fn_like_arguments(&self, node: hir::map::Node) -> (Span, Vec) { + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + pub fn get_fn_like_arguments(&self, node: hir::map::Node) -> (Span, Vec) { match node { hir::map::NodeExpr(&hir::Expr { node: hir::ExprClosure(_, ref _decl, id, span, _), @@ -829,7 +833,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .. } = arg.pat.clone().into_inner() { ArgKind::Tuple( - span, + Some(span), args.iter().map(|pat| { let snippet = self.tcx.sess.codemap() .span_to_snippet(pat.span).unwrap(); @@ -862,7 +866,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { (self.tcx.sess.codemap().def_span(span), decl.inputs.iter() .map(|arg| match arg.clone().into_inner().node { hir::TyTup(ref tys) => ArgKind::Tuple( - arg.span, + Some(arg.span), tys.iter() .map(|_| ("_".to_owned(), "_".to_owned())) .collect::>(), @@ -874,7 +878,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } - fn report_arg_count_mismatch( + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + pub fn report_arg_count_mismatch( &self, span: Span, found_span: Option, @@ -1363,13 +1370,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } -enum ArgKind { +/// Summarizes information +pub enum ArgKind { + /// An argument of non-tuple type. Parameters are (name, ty) Arg(String, String), - Tuple(Span, Vec<(String, String)>), + + /// An argument of tuple type. For a "found" argument, the span is + /// the locationo in the source of the pattern. For a "expected" + /// argument, it will be None. The vector is a list of (name, ty) + /// strings for the components of the tuple. + Tuple(Option, Vec<(String, String)>), } impl ArgKind { fn empty() -> ArgKind { ArgKind::Arg("_".to_owned(), "_".to_owned()) } + + /// Creates an `ArgKind` from the expected type of an + /// argument. This has no name (`_`) and no source spans.. + pub fn from_expected_ty(t: Ty<'_>) -> ArgKind { + match t.sty { + ty::TyTuple(ref tys, _) => ArgKind::Tuple( + None, + tys.iter() + .map(|ty| ("_".to_owned(), format!("{}", ty.sty))) + .collect::>() + ), + _ => ArgKind::Arg("_".to_owned(), format!("{}", t.sty)), + } + } } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 80819a86b7c46..a80e91df03de1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -49,7 +49,7 @@ pub use self::util::SupertraitDefIds; pub use self::util::transitive_bounds; mod coherence; -mod error_reporting; +pub mod error_reporting; mod fulfill; mod project; mod object_safety; diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 5e9c283a0c6e8..794d466ee7cdb 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -17,14 +17,24 @@ use rustc::hir::def_id::DefId; use rustc::infer::{InferOk, InferResult}; use rustc::infer::LateBoundRegionConversionTime; use rustc::infer::type_variable::TypeVariableOrigin; +use rustc::traits::error_reporting::ArgKind; use rustc::ty::{self, ToPolyTraitRef, Ty}; use rustc::ty::subst::Substs; use rustc::ty::TypeFoldable; use std::cmp; use std::iter; use syntax::abi::Abi; +use syntax::codemap::Span; use rustc::hir; +/// What signature do we *expect* the closure to have from context? +#[derive(Debug)] +struct ExpectedSig<'tcx> { + /// Span that gave us this expectation, if we know that. + cause_span: Option, + sig: ty::FnSig<'tcx>, +} + struct ClosureSignatures<'tcx> { bound_sig: ty::PolyFnSig<'tcx>, liberated_sig: ty::FnSig<'tcx>, @@ -63,7 +73,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { decl: &'gcx hir::FnDecl, body: &'gcx hir::Body, gen: Option, - expected_sig: Option>, + expected_sig: Option>, ) -> Ty<'tcx> { debug!( "check_closure(opt_kind={:?}, expected_sig={:?})", @@ -160,10 +170,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { closure_type } + /// Given the expected type, figures out what it can about this closure we + /// are about to type check: fn deduce_expectations_from_expected_type( &self, expected_ty: Ty<'tcx>, - ) -> (Option>, Option) { + ) -> (Option>, Option) { debug!( "deduce_expectations_from_expected_type(expected_ty={:?})", expected_ty @@ -175,7 +187,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .projection_bounds() .filter_map(|pb| { let pb = pb.with_self_ty(self.tcx, self.tcx.types.err); - self.deduce_sig_from_projection(&pb) + self.deduce_sig_from_projection(None, &pb) }) .next(); let kind = object_type @@ -184,7 +196,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { (sig, kind) } ty::TyInfer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid), - ty::TyFnPtr(sig) => (Some(sig.skip_binder().clone()), Some(ty::ClosureKind::Fn)), + ty::TyFnPtr(sig) => { + let expected_sig = ExpectedSig { + cause_span: None, + sig: sig.skip_binder().clone(), + }; + (Some(expected_sig), Some(ty::ClosureKind::Fn)) + } _ => (None, None), } } @@ -192,7 +210,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn deduce_expectations_from_obligations( &self, expected_vid: ty::TyVid, - ) -> (Option>, Option) { + ) -> (Option>, Option) { let fulfillment_cx = self.fulfillment_cx.borrow(); // Here `expected_ty` is known to be a type inference variable. @@ -212,7 +230,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty::Predicate::Projection(ref proj_predicate) => { let trait_ref = proj_predicate.to_poly_trait_ref(self.tcx); self.self_type_matches_expected_vid(trait_ref, expected_vid) - .and_then(|_| self.deduce_sig_from_projection(proj_predicate)) + .and_then(|_| { + self.deduce_sig_from_projection( + Some(obligation.cause.span), + proj_predicate, + ) + }) } _ => None, } @@ -262,10 +285,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// Given a projection like "::Result == Y", we can deduce /// everything we need to know about a closure. + /// + /// The `cause_span` should be the span that caused us to + /// have this expected signature, or `None` if we can't readily + /// know that. fn deduce_sig_from_projection( &self, + cause_span: Option, projection: &ty::PolyProjectionPredicate<'tcx>, - ) -> Option> { + ) -> Option> { let tcx = self.tcx; debug!("deduce_sig_from_projection({:?})", projection); @@ -297,16 +325,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ret_param_ty ); - let fn_sig = self.tcx.mk_fn_sig( + let sig = self.tcx.mk_fn_sig( input_tys.cloned(), ret_param_ty, false, hir::Unsafety::Normal, Abi::Rust, ); - debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig); + debug!("deduce_sig_from_projection: sig {:?}", sig); - Some(fn_sig) + Some(ExpectedSig { cause_span, sig }) } fn self_type_matches_expected_vid( @@ -330,7 +358,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr_def_id: DefId, decl: &hir::FnDecl, body: &hir::Body, - expected_sig: Option>, + expected_sig: Option>, ) -> ClosureSignatures<'tcx> { if let Some(e) = expected_sig { self.sig_of_closure_with_expectation(expr_def_id, decl, body, e) @@ -406,7 +434,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr_def_id: DefId, decl: &hir::FnDecl, body: &hir::Body, - expected_sig: ty::FnSig<'tcx>, + expected_sig: ExpectedSig<'tcx>, ) -> ClosureSignatures<'tcx> { debug!( "sig_of_closure_with_expectation(expected_sig={:?})", @@ -416,20 +444,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Watch out for some surprises and just ignore the // expectation if things don't see to match up with what we // expect. - if expected_sig.variadic != decl.variadic { - return self.sig_of_closure_no_expectation(expr_def_id, decl, body); - } else if expected_sig.inputs_and_output.len() != decl.inputs.len() + 1 { - // we could probably handle this case more gracefully + if expected_sig.sig.variadic != decl.variadic { return self.sig_of_closure_no_expectation(expr_def_id, decl, body); + } else if expected_sig.sig.inputs_and_output.len() != decl.inputs.len() + 1 { + return self.sig_of_closure_with_mismatched_number_of_arguments( + expr_def_id, + decl, + body, + expected_sig, + ); } // Create a `PolyFnSig`. Note the oddity that late bound // regions appearing free in `expected_sig` are now bound up // in this binder we are creating. - assert!(!expected_sig.has_regions_escaping_depth(1)); + assert!(!expected_sig.sig.has_regions_escaping_depth(1)); let bound_sig = ty::Binder(self.tcx.mk_fn_sig( - expected_sig.inputs().iter().cloned(), - expected_sig.output(), + expected_sig.sig.inputs().iter().cloned(), + expected_sig.sig.output(), decl.variadic, hir::Unsafety::Normal, Abi::RustCall, @@ -455,6 +487,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { closure_sigs } + fn sig_of_closure_with_mismatched_number_of_arguments( + &self, + expr_def_id: DefId, + decl: &hir::FnDecl, + body: &hir::Body, + expected_sig: ExpectedSig<'tcx>, + ) -> ClosureSignatures<'tcx> { + let expr_map_node = self.tcx.hir.get_if_local(expr_def_id).unwrap(); + let expected_args: Vec<_> = expected_sig + .sig + .inputs() + .iter() + .map(|ty| ArgKind::from_expected_ty(ty)) + .collect(); + let (closure_span, found_args) = self.get_fn_like_arguments(expr_map_node); + let expected_span = expected_sig.cause_span.unwrap_or(closure_span); + self.report_arg_count_mismatch( + expected_span, + Some(closure_span), + expected_args, + found_args, + true, + ).emit(); + + let error_sig = self.error_sig_of_closure(decl); + + self.closure_sigs(expr_def_id, body, error_sig) + } + /// Enforce the user's types against the expectation. See /// `sig_of_closure_with_expectation` for details on the overall /// strategy. @@ -560,6 +621,38 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { result } + /// Converts the types that the user supplied, in case that doing + /// so should yield an error, but returns back a signature where + /// all parameters are of type `TyErr`. + fn error_sig_of_closure(&self, decl: &hir::FnDecl) -> ty::PolyFnSig<'tcx> { + let astconv: &AstConv = self; + + let supplied_arguments = decl.inputs.iter().map(|a| { + // Convert the types that the user supplied (if any), but ignore them. + astconv.ast_ty_to_ty(a); + self.tcx.types.err + }); + + match decl.output { + hir::Return(ref output) => { + astconv.ast_ty_to_ty(&output); + } + hir::DefaultReturn(_) => {} + } + + let result = ty::Binder(self.tcx.mk_fn_sig( + supplied_arguments, + self.tcx.types.err, + decl.variadic, + hir::Unsafety::Normal, + Abi::RustCall, + )); + + debug!("supplied_sig_of_closure: result={:?}", result); + + result + } + fn closure_sigs( &self, expr_def_id: DefId, diff --git a/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.rs b/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.rs new file mode 100644 index 0000000000000..b6463ca067b7f --- /dev/null +++ b/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.rs @@ -0,0 +1,29 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #47244: in this specific scenario, when the +// expected type indicated 1 argument but the closure takes two, we +// would (early on) create type variables for the type of `b`. If the +// user then attempts to invoke a method on `b`, we would get an error +// saying that the type of `b` must be known, which was not very +// helpful. + +use std::collections::HashMap; +fn main() { + + let m = HashMap::new(); + m.insert( "foo", "bar" ); + + m.iter().map( |_, b| { + //~^ ERROR closure is expected to take a single 2-tuple + + b.to_string() + }); +} diff --git a/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr b/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr new file mode 100644 index 0000000000000..34934b8d19598 --- /dev/null +++ b/src/test/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr @@ -0,0 +1,14 @@ +error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments + --> $DIR/closure-arg-count-expected-type-issue-47244.rs:24:14 + | +24 | m.iter().map( |_, b| { + | ^^^ ------ takes 2 distinct arguments + | | + | expected closure that takes a single 2-tuple as argument +help: change the closure to accept a tuple instead of individual arguments + | +24 | m.iter().map( |(_, b)| { + | ^^^^^^^^ + +error: aborting due to previous error + From 335e25fd79e57c4cd7453c0af25e5c6bd0406602 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 13 Feb 2018 15:50:37 +0100 Subject: [PATCH 08/42] incr.comp.: Don't keep RefCells in on-disk-cache borrowed in order to allow for recursive invocations. --- src/librustc/ty/maps/on_disk_cache.rs | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index 56ed0f9106f30..17b44f6959f2f 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -347,22 +347,21 @@ impl<'sess> OnDiskCache<'sess> { return None }; - let mut cnum_map = self.cnum_map.borrow_mut(); - if cnum_map.is_none() { + // Initialize the cnum_map if it is not initialized yet. + if self.cnum_map.borrow().is_none() { + let mut cnum_map = self.cnum_map.borrow_mut(); *cnum_map = Some(Self::compute_cnum_map(tcx, &self.prev_cnums[..])); } - - let mut synthetic_expansion_infos = self.synthetic_expansion_infos.borrow_mut(); - let mut file_index_to_file = self.file_index_to_file.borrow_mut(); + let cnum_map = self.cnum_map.borrow(); let mut decoder = CacheDecoder { tcx, opaque: opaque::Decoder::new(&self.serialized_data[..], pos.to_usize()), codemap: self.codemap, cnum_map: cnum_map.as_ref().unwrap(), - file_index_to_file: &mut file_index_to_file, + file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, - synthetic_expansion_infos: &mut synthetic_expansion_infos, + synthetic_expansion_infos: &self.synthetic_expansion_infos, }; match decode_tagged(&mut decoder, dep_node_index) { @@ -421,21 +420,21 @@ struct CacheDecoder<'a, 'tcx: 'a, 'x> { opaque: opaque::Decoder<'x>, codemap: &'x CodeMap, cnum_map: &'x IndexVec>, - synthetic_expansion_infos: &'x mut FxHashMap, - file_index_to_file: &'x mut FxHashMap>, + synthetic_expansion_infos: &'x RefCell>, + file_index_to_file: &'x RefCell>>, file_index_to_stable_id: &'x FxHashMap, } impl<'a, 'tcx, 'x> CacheDecoder<'a, 'tcx, 'x> { - fn file_index_to_file(&mut self, index: FileMapIndex) -> Rc { + fn file_index_to_file(&self, index: FileMapIndex) -> Rc { let CacheDecoder { - ref mut file_index_to_file, + ref file_index_to_file, ref file_index_to_stable_id, ref codemap, .. } = *self; - file_index_to_file.entry(index).or_insert_with(|| { + file_index_to_file.borrow_mut().entry(index).or_insert_with(|| { let stable_id = file_index_to_stable_id[&index]; codemap.filemap_by_stable_id(stable_id) .expect("Failed to lookup FileMap in new context.") @@ -572,19 +571,24 @@ impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { let pos = AbsoluteBytePos::new(self.opaque.position()); let expn_info: ExpnInfo = Decodable::decode(self)?; let ctxt = SyntaxContext::allocate_directly(expn_info); - self.synthetic_expansion_infos.insert(pos, ctxt); + self.synthetic_expansion_infos.borrow_mut().insert(pos, ctxt); ctxt } TAG_EXPANSION_INFO_SHORTHAND => { let pos = AbsoluteBytePos::decode(self)?; - if let Some(ctxt) = self.synthetic_expansion_infos.get(&pos).cloned() { + let cached_ctxt = self.synthetic_expansion_infos + .borrow() + .get(&pos) + .cloned(); + + if let Some(ctxt) = cached_ctxt { ctxt } else { let expn_info = self.with_position(pos.to_usize(), |this| { ExpnInfo::decode(this) })?; let ctxt = SyntaxContext::allocate_directly(expn_info); - self.synthetic_expansion_infos.insert(pos, ctxt); + self.synthetic_expansion_infos.borrow_mut().insert(pos, ctxt); ctxt } } From 3118cbe41c7ce50c3451fd4575ca04a83b0d3e05 Mon Sep 17 00:00:00 2001 From: bobtwinkles Date: Tue, 13 Feb 2018 20:28:10 -0500 Subject: [PATCH 09/42] Allow two-phase borrows of &mut self in ops We need two-phase borrows of ops to be in the initial NLL release since without them lots of existing code will break. Fixes #48129 --- src/librustc_typeck/check/op.rs | 14 +++-- .../borrowck/two-phase-nonrecv-autoref.rs | 53 ------------------- .../run-pass/borrowck/two-phase-bin-ops.rs | 49 +++++++++++++++++ 3 files changed, 55 insertions(+), 61 deletions(-) create mode 100644 src/test/run-pass/borrowck/two-phase-bin-ops.rs diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index a6776a0fe8612..a3dbf344ab7e0 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -201,10 +201,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mutbl = match mt.mutbl { hir::MutImmutable => AutoBorrowMutability::Immutable, hir::MutMutable => AutoBorrowMutability::Mutable { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded binary ops. - allow_two_phase_borrow: false, + // Allow two-phase borrows for binops in initial deployment + // since they desugar to methods + allow_two_phase_borrow: true, } }; let autoref = Adjustment { @@ -219,10 +218,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mutbl = match mt.mutbl { hir::MutImmutable => AutoBorrowMutability::Immutable, hir::MutMutable => AutoBorrowMutability::Mutable { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded binary ops. - allow_two_phase_borrow: false, + // Allow two-phase borrows for binops in initial deployment + // since they desugar to methods + allow_two_phase_borrow: true, } }; let autoref = Adjustment { diff --git a/src/test/compile-fail/borrowck/two-phase-nonrecv-autoref.rs b/src/test/compile-fail/borrowck/two-phase-nonrecv-autoref.rs index 795d45a776db5..a57e2fe1cc6d3 100644 --- a/src/test/compile-fail/borrowck/two-phase-nonrecv-autoref.rs +++ b/src/test/compile-fail/borrowck/two-phase-nonrecv-autoref.rs @@ -30,8 +30,6 @@ // #![feature(rustc_attrs)] use std::ops::{Index, IndexMut}; -use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign}; -use std::ops::{BitAndAssign, BitOrAssign, BitXorAssign, ShlAssign, ShrAssign}; // This is case outlined by Niko that we want to ensure we reject // (at least initially). @@ -186,56 +184,6 @@ fn coerce_index_op() { //[nll]~^^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502] } -struct A(i32); - -macro_rules! trivial_binop { - ($Trait:ident, $m:ident) => { - impl $Trait for A { fn $m(&mut self, rhs: i32) { self.0 = rhs; } } - } -} - -trivial_binop!(AddAssign, add_assign); -trivial_binop!(SubAssign, sub_assign); -trivial_binop!(MulAssign, mul_assign); -trivial_binop!(DivAssign, div_assign); -trivial_binop!(RemAssign, rem_assign); -trivial_binop!(BitAndAssign, bitand_assign); -trivial_binop!(BitOrAssign, bitor_assign); -trivial_binop!(BitXorAssign, bitxor_assign); -trivial_binop!(ShlAssign, shl_assign); -trivial_binop!(ShrAssign, shr_assign); - -fn overloaded_binops() { - let mut a = A(10); - a += a.0; - //[lxl]~^ ERROR cannot use `a.0` because it was mutably borrowed - //[nll]~^^ ERROR cannot use `a.0` because it was mutably borrowed - a -= a.0; - //[lxl]~^ ERROR cannot use `a.0` because it was mutably borrowed - //[nll]~^^ ERROR cannot use `a.0` because it was mutably borrowed - a *= a.0; - //[lxl]~^ ERROR cannot use `a.0` because it was mutably borrowed - //[nll]~^^ ERROR cannot use `a.0` because it was mutably borrowed - a /= a.0; - //[lxl]~^ ERROR cannot use `a.0` because it was mutably borrowed - //[nll]~^^ ERROR cannot use `a.0` because it was mutably borrowed - a &= a.0; - //[lxl]~^ ERROR cannot use `a.0` because it was mutably borrowed - //[nll]~^^ ERROR cannot use `a.0` because it was mutably borrowed - a |= a.0; - //[lxl]~^ ERROR cannot use `a.0` because it was mutably borrowed - //[nll]~^^ ERROR cannot use `a.0` because it was mutably borrowed - a ^= a.0; - //[lxl]~^ ERROR cannot use `a.0` because it was mutably borrowed - //[nll]~^^ ERROR cannot use `a.0` because it was mutably borrowed - a <<= a.0; - //[lxl]~^ ERROR cannot use `a.0` because it was mutably borrowed - //[nll]~^^ ERROR cannot use `a.0` because it was mutably borrowed - a >>= a.0; - //[lxl]~^ ERROR cannot use `a.0` because it was mutably borrowed - //[nll]~^^ ERROR cannot use `a.0` because it was mutably borrowed -} - fn main() { // As a reminder, this is the basic case we want to ensure we handle. @@ -256,5 +204,4 @@ fn main() { coerce_unsized(); coerce_index_op(); - overloaded_binops(); } diff --git a/src/test/run-pass/borrowck/two-phase-bin-ops.rs b/src/test/run-pass/borrowck/two-phase-bin-ops.rs new file mode 100644 index 0000000000000..9bded41e1f933 --- /dev/null +++ b/src/test/run-pass/borrowck/two-phase-bin-ops.rs @@ -0,0 +1,49 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: lxl nll +//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows + +#![cfg_attr(nll, feature(nll))] + +use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign}; +use std::ops::{BitAndAssign, BitOrAssign, BitXorAssign, ShlAssign, ShrAssign}; + +struct A(i32); + +macro_rules! trivial_binop { + ($Trait:ident, $m:ident) => { + impl $Trait for A { fn $m(&mut self, rhs: i32) { self.0 = rhs; } } + } +} + +trivial_binop!(AddAssign, add_assign); +trivial_binop!(SubAssign, sub_assign); +trivial_binop!(MulAssign, mul_assign); +trivial_binop!(DivAssign, div_assign); +trivial_binop!(RemAssign, rem_assign); +trivial_binop!(BitAndAssign, bitand_assign); +trivial_binop!(BitOrAssign, bitor_assign); +trivial_binop!(BitXorAssign, bitxor_assign); +trivial_binop!(ShlAssign, shl_assign); +trivial_binop!(ShrAssign, shr_assign); + +fn main() { + let mut a = A(10); + a += a.0; + a -= a.0; + a *= a.0; + a /= a.0; + a &= a.0; + a |= a.0; + a ^= a.0; + a <<= a.0; + a >>= a.0; +} From 75f72c0de141573eef56f13fd48a3af12deaee4f Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 8 Feb 2018 15:40:27 -0800 Subject: [PATCH 10/42] Make nested impl Trait a hard error --- src/librustc_passes/ast_validation.rs | 69 ++++++++++++++++++ src/librustc_passes/diagnostics.rs | 1 + src/libsyntax/feature_gate.rs | 70 +------------------ .../compile-fail/impl-trait/where-allowed.rs | 4 +- src/test/run-pass/impl-trait/lifetimes.rs | 11 ++- src/test/ui/error-codes/E0657.rs | 6 +- .../nested_impl_trait.rs} | 10 +-- src/test/ui/nested_impl_trait.stderr | 50 +++++++++++++ 8 files changed, 138 insertions(+), 83 deletions(-) rename src/test/{compile-fail/feature-gate-nested_impl_trait.rs => ui/nested_impl_trait.rs} (80%) create mode 100644 src/test/ui/nested_impl_trait.stderr diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 6971033c8994b..826f27c2ddbf4 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -420,6 +420,75 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } +// Bans nested `impl Trait`, e.g. `impl Into`. +// Nested `impl Trait` _is_ allowed in associated type position, +// e.g `impl Iterator` +struct NestedImplTraitVisitor<'a> { + session: &'a Session, + outer_impl_trait: Option, +} + +impl<'a> NestedImplTraitVisitor<'a> { + fn with_impl_trait(&mut self, outer_impl_trait: Option, f: F) + where F: FnOnce(&mut NestedImplTraitVisitor<'a>) + { + let old_outer_impl_trait = self.outer_impl_trait; + self.outer_impl_trait = outer_impl_trait; + f(self); + self.outer_impl_trait = old_outer_impl_trait; + } +} + + +impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { + fn visit_ty(&mut self, t: &'a Ty) { + if let TyKind::ImplTrait(_) = t.node { + if let Some(outer_impl_trait) = self.outer_impl_trait { + struct_span_err!(self.session, t.span, E0666, + "nested `impl Trait` is not allowed") + .span_label(outer_impl_trait, "outer `impl Trait`") + .span_label(t.span, "devilishly nested `impl Trait` here") + .emit(); + + } + self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t)); + } else { + visit::walk_ty(self, t); + } + } + fn visit_path_parameters(&mut self, _: Span, path_parameters: &'a PathParameters) { + match *path_parameters { + PathParameters::AngleBracketed(ref params) => { + for type_ in ¶ms.types { + self.visit_ty(type_); + } + for type_binding in ¶ms.bindings { + // Type bindings such as `Item=impl Debug` in `Iterator` + // are allowed to contain nested `impl Trait`. + self.with_impl_trait(None, |this| visit::walk_ty(this, &type_binding.ty)); + } + } + PathParameters::Parenthesized(ref params) => { + for type_ in ¶ms.inputs { + self.visit_ty(type_); + } + if let Some(ref type_) = params.output { + // `-> Foo` syntax is essentially an associated type binding, + // so it is also allowed to contain nested `impl Trait`. + self.with_impl_trait(None, |this| visit::walk_ty(this, type_)); + } + } + } + } +} + + pub fn check_crate(session: &Session, krate: &Crate) { + visit::walk_crate( + &mut NestedImplTraitVisitor { + session, + outer_impl_trait: None, + }, krate); + visit::walk_crate(&mut AstValidator { session: session }, krate) } diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 743f7b7326e9e..6dfc52f842e16 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -320,4 +320,5 @@ register_diagnostics! { E0567, // auto traits can not have generic parameters E0568, // auto traits can not have super traits E0642, // patterns aren't allowed in methods without bodies + E0666, // nested `impl Trait` is illegal } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ea916d5168c33..a3eba63706cb0 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -432,9 +432,6 @@ declare_features! ( // `foo.rs` as an alternative to `foo/mod.rs` (active, non_modrs_mods, "1.24.0", Some(44660)), - // Nested `impl Trait` - (active, nested_impl_trait, "1.24.0", Some(34511)), - // Termination trait in main (RFC 1937) (active, termination_trait, "1.24.0", Some(43301)), @@ -1352,73 +1349,8 @@ fn contains_novel_literal(item: &ast::MetaItem) -> bool { } } -// Bans nested `impl Trait`, e.g. `impl Into`. -// Nested `impl Trait` _is_ allowed in associated type position, -// e.g `impl Iterator` -struct NestedImplTraitVisitor<'a> { - context: &'a Context<'a>, - is_in_impl_trait: bool, -} - -impl<'a> NestedImplTraitVisitor<'a> { - fn with_impl_trait(&mut self, is_in_impl_trait: bool, f: F) - where F: FnOnce(&mut NestedImplTraitVisitor<'a>) - { - let old_is_in_impl_trait = self.is_in_impl_trait; - self.is_in_impl_trait = is_in_impl_trait; - f(self); - self.is_in_impl_trait = old_is_in_impl_trait; - } -} - - -impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { - fn visit_ty(&mut self, t: &'a ast::Ty) { - if let ast::TyKind::ImplTrait(_) = t.node { - if self.is_in_impl_trait { - gate_feature_post!(&self, nested_impl_trait, t.span, - "nested `impl Trait` is experimental" - ); - } - self.with_impl_trait(true, |this| visit::walk_ty(this, t)); - } else { - visit::walk_ty(self, t); - } - } - fn visit_path_parameters(&mut self, _: Span, path_parameters: &'a ast::PathParameters) { - match *path_parameters { - ast::PathParameters::AngleBracketed(ref params) => { - for type_ in ¶ms.types { - self.visit_ty(type_); - } - for type_binding in ¶ms.bindings { - // Type bindings such as `Item=impl Debug` in `Iterator` - // are allowed to contain nested `impl Trait`. - self.with_impl_trait(false, |this| visit::walk_ty(this, &type_binding.ty)); - } - } - ast::PathParameters::Parenthesized(ref params) => { - for type_ in ¶ms.inputs { - self.visit_ty(type_); - } - if let Some(ref type_) = params.output { - // `-> Foo` syntax is essentially an associated type binding, - // so it is also allowed to contain nested `impl Trait`. - self.with_impl_trait(false, |this| visit::walk_ty(this, type_)); - } - } - } - } -} - impl<'a> PostExpansionVisitor<'a> { - fn whole_crate_feature_gates(&mut self, krate: &ast::Crate) { - visit::walk_crate( - &mut NestedImplTraitVisitor { - context: self.context, - is_in_impl_trait: false, - }, krate); - + fn whole_crate_feature_gates(&mut self, _krate: &ast::Crate) { for &(ident, span) in &*self.context.parse_sess.non_modrs_mods.borrow() { if !span.allows_unstable() { let cx = &self.context; diff --git a/src/test/compile-fail/impl-trait/where-allowed.rs b/src/test/compile-fail/impl-trait/where-allowed.rs index a9fe1e04664e9..52c5471681df3 100644 --- a/src/test/compile-fail/impl-trait/where-allowed.rs +++ b/src/test/compile-fail/impl-trait/where-allowed.rs @@ -10,7 +10,7 @@ //! A simple test for testing many permutations of allowedness of //! impl Trait -#![feature(conservative_impl_trait, nested_impl_trait, universal_impl_trait, dyn_trait)] +#![feature(conservative_impl_trait, universal_impl_trait, dyn_trait)] use std::fmt::Debug; // Allowed @@ -60,6 +60,7 @@ fn in_dyn_Fn_return_in_return() -> &'static dyn Fn() -> impl Debug { panic!() } // Disallowed fn in_impl_Fn_parameter_in_parameters(_: &impl Fn(impl Debug)) { panic!() } //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +//~^^ ERROR nested `impl Trait` is not allowed // Disallowed fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() } @@ -68,6 +69,7 @@ fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() } // Disallowed fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic!() } //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +//~^^ ERROR nested `impl Trait` is not allowed // Disallowed fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { panic!() } diff --git a/src/test/run-pass/impl-trait/lifetimes.rs b/src/test/run-pass/impl-trait/lifetimes.rs index 1f2d76f289472..4d40e707ddcb9 100644 --- a/src/test/run-pass/impl-trait/lifetimes.rs +++ b/src/test/run-pass/impl-trait/lifetimes.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(conservative_impl_trait, underscore_lifetimes, universal_impl_trait, nested_impl_trait)] +#![feature(conservative_impl_trait, underscore_lifetimes, universal_impl_trait)] #![allow(warnings)] use std::fmt::Debug; @@ -55,12 +55,11 @@ fn pass_through_elision_with_fn_ptr(x: &fn(&u32) -> &u32) -> impl Into<&fn(&u32) fn pass_through_elision_with_fn_path &u32>( x: &T -) -> impl Into<&impl Fn(&u32) -> &u32> { x } +) -> &impl Fn(&u32) -> &u32 { x } -fn foo(x: &impl Debug) -> impl Into<&impl Debug> { x } -fn foo_explicit_lifetime<'a>(x: &'a impl Debug) -> impl Into<&'a impl Debug> { x } -fn foo_no_outer_impl(x: &impl Debug) -> &impl Debug { x } -fn foo_explicit_arg(x: &T) -> impl Into<&impl Debug> { x } +fn foo(x: &impl Debug) -> &impl Debug { x } +fn foo_explicit_lifetime<'a>(x: &'a impl Debug) -> &'a impl Debug { x } +fn foo_explicit_arg(x: &T) -> &impl Debug { x } fn mixed_lifetimes<'a>() -> impl for<'b: 'a> Fn(&'b u32) { |_| () } fn mixed_as_static() -> impl Fn(&'static u32) { mixed_lifetimes() } diff --git a/src/test/ui/error-codes/E0657.rs b/src/test/ui/error-codes/E0657.rs index 4595e413081a5..31b3acd86ef55 100644 --- a/src/test/ui/error-codes/E0657.rs +++ b/src/test/ui/error-codes/E0657.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![allow(warnings)] -#![feature(conservative_impl_trait, nested_impl_trait)] +#![feature(conservative_impl_trait)] trait Id {} trait Lt<'a> {} @@ -17,7 +17,7 @@ impl<'a> Lt<'a> for () {} impl Id for T {} fn free_fn_capture_hrtb_in_impl_trait() - -> impl for<'a> Id> + -> Box Id>> //~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level [E0657] { () @@ -26,7 +26,7 @@ fn free_fn_capture_hrtb_in_impl_trait() struct Foo; impl Foo { fn impl_fn_capture_hrtb_in_impl_trait() - -> impl for<'a> Id> + -> Box Id>> //~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level { () diff --git a/src/test/compile-fail/feature-gate-nested_impl_trait.rs b/src/test/ui/nested_impl_trait.rs similarity index 80% rename from src/test/compile-fail/feature-gate-nested_impl_trait.rs rename to src/test/ui/nested_impl_trait.rs index 7c35263d05dd7..f6302c0f3b3e2 100644 --- a/src/test/compile-fail/feature-gate-nested_impl_trait.rs +++ b/src/test/ui/nested_impl_trait.rs @@ -14,18 +14,19 @@ use std::fmt::Debug; fn fine(x: impl Into) -> impl Into { x } fn bad_in_ret_position(x: impl Into) -> impl Into { x } -//~^ ERROR nested `impl Trait` is experimental +//~^ ERROR nested `impl Trait` is not allowed fn bad_in_fn_syntax(x: fn() -> impl Into) {} -//~^ ERROR nested `impl Trait` is experimental +//~^ ERROR nested `impl Trait` is not allowed +//~^^ `impl Trait` not allowed fn bad_in_arg_position(_: impl Into) { } -//~^ ERROR nested `impl Trait` is experimental +//~^ ERROR nested `impl Trait` is not allowed struct X; impl X { fn bad(x: impl Into) -> impl Into { x } - //~^ ERROR nested `impl Trait` is experimental + //~^ ERROR nested `impl Trait` is not allowed } fn allowed_in_assoc_type() -> impl Iterator { @@ -33,6 +34,7 @@ fn allowed_in_assoc_type() -> impl Iterator { } fn allowed_in_ret_type() -> impl Fn() -> impl Into { +//~^ `impl Trait` not allowed || 5 } diff --git a/src/test/ui/nested_impl_trait.stderr b/src/test/ui/nested_impl_trait.stderr new file mode 100644 index 0000000000000..6649f9ab99cc1 --- /dev/null +++ b/src/test/ui/nested_impl_trait.stderr @@ -0,0 +1,50 @@ +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/nested_impl_trait.rs:16:56 + | +16 | fn bad_in_ret_position(x: impl Into) -> impl Into { x } + | ----------^^^^^^^^^^- + | | | + | | devilishly nested `impl Trait` here + | outer `impl Trait` + +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/nested_impl_trait.rs:19:42 + | +19 | fn bad_in_fn_syntax(x: fn() -> impl Into) {} + | ----------^^^^^^^^^^- + | | | + | | devilishly nested `impl Trait` here + | outer `impl Trait` + +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/nested_impl_trait.rs:23:37 + | +23 | fn bad_in_arg_position(_: impl Into) { } + | ----------^^^^^^^^^^- + | | | + | | devilishly nested `impl Trait` here + | outer `impl Trait` + +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/nested_impl_trait.rs:28:44 + | +28 | fn bad(x: impl Into) -> impl Into { x } + | ----------^^^^^^^^^^- + | | | + | | devilishly nested `impl Trait` here + | outer `impl Trait` + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/nested_impl_trait.rs:19:32 + | +19 | fn bad_in_fn_syntax(x: fn() -> impl Into) {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/nested_impl_trait.rs:36:42 + | +36 | fn allowed_in_ret_type() -> impl Fn() -> impl Into { + | ^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + From 70e1f4fc6d951dfc0547ff5acb3d8780d16635e6 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 8 Feb 2018 16:50:14 -0800 Subject: [PATCH 11/42] Disallow projections from impl Trait types --- src/librustc_passes/ast_validation.rs | 68 ++++++++++++++++++++++- src/librustc_passes/diagnostics.rs | 1 + src/test/ui/impl_trait_projections.rs | 43 ++++++++++++++ src/test/ui/impl_trait_projections.stderr | 28 ++++++++++ 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/impl_trait_projections.rs create mode 100644 src/test/ui/impl_trait_projections.stderr diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 826f27c2ddbf4..14ea5c0ce7982 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -447,7 +447,7 @@ impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { struct_span_err!(self.session, t.span, E0666, "nested `impl Trait` is not allowed") .span_label(outer_impl_trait, "outer `impl Trait`") - .span_label(t.span, "devilishly nested `impl Trait` here") + .span_label(t.span, "nested `impl Trait` here") .emit(); } @@ -482,6 +482,66 @@ impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { } } +// Bans `impl Trait` in path projections like `::Item` or `Foo::Bar`. +struct ImplTraitProjectionVisitor<'a> { + session: &'a Session, + is_banned: bool, +} + +impl<'a> ImplTraitProjectionVisitor<'a> { + fn with_ban(&mut self, f: F) + where F: FnOnce(&mut ImplTraitProjectionVisitor<'a>) + { + let old_is_banned = self.is_banned; + self.is_banned = true; + f(self); + self.is_banned = old_is_banned; + } +} + +impl<'a> Visitor<'a> for ImplTraitProjectionVisitor<'a> { + fn visit_ty(&mut self, t: &'a Ty) { + match t.node { + TyKind::ImplTrait(_) => { + if self.is_banned { + struct_span_err!(self.session, t.span, E0667, + "`impl Trait` is not allowed in path parameters") + .emit(); + } + } + TyKind::Path(ref qself, ref path) => { + // We allow these: + // - `Option` + // - `option::Option` + // - `option::Option::Foo + // + // But not these: + // - `::Foo` + // - `option::Option::Foo`. + // + // To implement this, we disallow `impl Trait` from `qself` + // (for cases like `::Foo>`) + // but we allow `impl Trait` in `PathParameters` + // iff there are no more PathSegments. + if let Some(ref qself) = *qself { + // `impl Trait` in `qself` is always illegal + self.with_ban(|this| this.visit_ty(&qself.ty)); + } + + for (i, segment) in path.segments.iter().enumerate() { + // Allow `impl Trait` iff we're on the final path segment + if i == (path.segments.len() - 1) { + visit::walk_path_segment(self, path.span, segment); + } else { + self.with_ban(|this| + visit::walk_path_segment(this, path.span, segment)); + } + } + } + _ => visit::walk_ty(self, t), + } + } +} pub fn check_crate(session: &Session, krate: &Crate) { visit::walk_crate( @@ -490,5 +550,11 @@ pub fn check_crate(session: &Session, krate: &Crate) { outer_impl_trait: None, }, krate); + visit::walk_crate( + &mut ImplTraitProjectionVisitor { + session, + is_banned: false, + }, krate); + visit::walk_crate(&mut AstValidator { session: session }, krate) } diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 6dfc52f842e16..980808a6905c1 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -321,4 +321,5 @@ register_diagnostics! { E0568, // auto traits can not have super traits E0642, // patterns aren't allowed in methods without bodies E0666, // nested `impl Trait` is illegal + E0667, // `impl Trait` in projections } diff --git a/src/test/ui/impl_trait_projections.rs b/src/test/ui/impl_trait_projections.rs new file mode 100644 index 0000000000000..e34b7799fb724 --- /dev/null +++ b/src/test/ui/impl_trait_projections.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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(conservative_impl_trait, universal_impl_trait)] + +use std::fmt::Debug; +use std::option; + +fn parametrized_type_is_allowed() -> Option { + Some(5i32) +} + +fn path_parametrized_type_is_allowed() -> option::Option { + Some(5i32) +} + +fn projection_is_disallowed(x: impl Iterator) -> ::Item { +//~^ ERROR `impl Trait` is not allowed in path parameters +//~^^ ERROR ambiguous associated type + x.next().unwrap() +} + +fn projection_with_named_trait_is_disallowed(x: impl Iterator) + -> ::Item +//~^ ERROR `impl Trait` is not allowed in path parameters +{ + x.next().unwrap() +} + +fn projection_with_named_trait_inside_path_is_disallowed() + -> <::std::ops::Range as Iterator>::Item +//~^ ERROR `impl Trait` is not allowed in path parameters +{ + (1i32..100).next().unwrap() +} + +fn main() {} diff --git a/src/test/ui/impl_trait_projections.stderr b/src/test/ui/impl_trait_projections.stderr new file mode 100644 index 0000000000000..2e8bfc931f82e --- /dev/null +++ b/src/test/ui/impl_trait_projections.stderr @@ -0,0 +1,28 @@ +error[E0667]: `impl Trait` is not allowed in path parameters + --> $DIR/impl_trait_projections.rs:23:51 + | +23 | fn projection_is_disallowed(x: impl Iterator) -> ::Item { + | ^^^^^^^^^^^^^ + +error[E0667]: `impl Trait` is not allowed in path parameters + --> $DIR/impl_trait_projections.rs:30:9 + | +30 | -> ::Item + | ^^^^^^^^^^^^^ + +error[E0667]: `impl Trait` is not allowed in path parameters + --> $DIR/impl_trait_projections.rs:37:27 + | +37 | -> <::std::ops::Range as Iterator>::Item + | ^^^^^^^^^^ + +error[E0223]: ambiguous associated type + --> $DIR/impl_trait_projections.rs:23:50 + | +23 | fn projection_is_disallowed(x: impl Iterator) -> ::Item { + | ^^^^^^^^^^^^^^^^^^^^^ ambiguous associated type + | + = note: specify the type using the syntax `::Item` + +error: aborting due to 4 previous errors + From f1fbf79223aca762c42c6674d125c4c5223f9949 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 13 Feb 2018 17:46:33 -0800 Subject: [PATCH 12/42] Amend nested impl Trait error message --- src/test/ui/nested_impl_trait.stderr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/ui/nested_impl_trait.stderr b/src/test/ui/nested_impl_trait.stderr index 6649f9ab99cc1..094926120cdee 100644 --- a/src/test/ui/nested_impl_trait.stderr +++ b/src/test/ui/nested_impl_trait.stderr @@ -4,7 +4,7 @@ error[E0666]: nested `impl Trait` is not allowed 16 | fn bad_in_ret_position(x: impl Into) -> impl Into { x } | ----------^^^^^^^^^^- | | | - | | devilishly nested `impl Trait` here + | | nested `impl Trait` here | outer `impl Trait` error[E0666]: nested `impl Trait` is not allowed @@ -13,7 +13,7 @@ error[E0666]: nested `impl Trait` is not allowed 19 | fn bad_in_fn_syntax(x: fn() -> impl Into) {} | ----------^^^^^^^^^^- | | | - | | devilishly nested `impl Trait` here + | | nested `impl Trait` here | outer `impl Trait` error[E0666]: nested `impl Trait` is not allowed @@ -22,7 +22,7 @@ error[E0666]: nested `impl Trait` is not allowed 23 | fn bad_in_arg_position(_: impl Into) { } | ----------^^^^^^^^^^- | | | - | | devilishly nested `impl Trait` here + | | nested `impl Trait` here | outer `impl Trait` error[E0666]: nested `impl Trait` is not allowed @@ -31,7 +31,7 @@ error[E0666]: nested `impl Trait` is not allowed 28 | fn bad(x: impl Into) -> impl Into { x } | ----------^^^^^^^^^^- | | | - | | devilishly nested `impl Trait` here + | | nested `impl Trait` here | outer `impl Trait` error[E0562]: `impl Trait` not allowed outside of function and inherent method return types From dbacf0c56ba2bd5ed38fc2a2e4bc20150271e7f2 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 13 Feb 2018 17:52:22 -0800 Subject: [PATCH 13/42] Test err on impl Trait projection within dyn Trait --- src/test/ui/impl_trait_projections.rs | 9 ++++++++- src/test/ui/impl_trait_projections.stderr | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/test/ui/impl_trait_projections.rs b/src/test/ui/impl_trait_projections.rs index e34b7799fb724..f69a78b1450f1 100644 --- a/src/test/ui/impl_trait_projections.rs +++ b/src/test/ui/impl_trait_projections.rs @@ -7,7 +7,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(conservative_impl_trait, universal_impl_trait)] +#![feature(dyn_trait, conservative_impl_trait, universal_impl_trait)] use std::fmt::Debug; use std::option; @@ -40,4 +40,11 @@ fn projection_with_named_trait_inside_path_is_disallowed() (1i32..100).next().unwrap() } +fn projection_from_impl_trait_inside_dyn_trait_is_disallowed() + -> as Iterator>::Item +//~^ ERROR `impl Trait` is not allowed in path parameters +{ + panic!() +} + fn main() {} diff --git a/src/test/ui/impl_trait_projections.stderr b/src/test/ui/impl_trait_projections.stderr index 2e8bfc931f82e..08de0eb99a307 100644 --- a/src/test/ui/impl_trait_projections.stderr +++ b/src/test/ui/impl_trait_projections.stderr @@ -16,6 +16,12 @@ error[E0667]: `impl Trait` is not allowed in path parameters 37 | -> <::std::ops::Range as Iterator>::Item | ^^^^^^^^^^ +error[E0667]: `impl Trait` is not allowed in path parameters + --> $DIR/impl_trait_projections.rs:44:29 + | +44 | -> as Iterator>::Item + | ^^^^^^^^^^ + error[E0223]: ambiguous associated type --> $DIR/impl_trait_projections.rs:23:50 | @@ -24,5 +30,5 @@ error[E0223]: ambiguous associated type | = note: specify the type using the syntax `::Item` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From 20dcc72127b53e1b7388f430f7ed26144fe5dcf4 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Wed, 14 Feb 2018 11:06:08 +0800 Subject: [PATCH 14/42] inform type annotations --- src/librustc_typeck/check/mod.rs | 4 +--- src/librustc_typeck/diagnostics.rs | 3 ++- .../epoch-raw-pointer-method-2015.rs | 2 +- src/test/compile-fail/issue-15965.rs | 2 +- src/test/compile-fail/issue-2151.rs | 2 +- src/test/compile-fail/match-vec-mismatch.rs | 2 +- src/test/compile-fail/pat-tuple-bad-type.rs | 2 +- .../unboxed-closures-failed-recursive-fn-2.rs | 2 +- src/test/ui/error-codes/E0619.rs | 19 ------------------- src/test/ui/error-codes/E0619.stderr | 8 -------- ...ference-variable-behind-raw-pointer.stderr | 2 +- .../span/issue-42234-unknown-receiver-type.rs | 4 ++-- .../issue-42234-unknown-receiver-type.stderr | 6 +++--- 13 files changed, 15 insertions(+), 43 deletions(-) delete mode 100644 src/test/ui/error-codes/E0619.rs delete mode 100644 src/test/ui/error-codes/E0619.stderr diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 165b499cc62aa..e760636230d18 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5086,9 +5086,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // If not, error. if alternative.is_ty_var() || alternative.references_error() { if !self.is_tainted_by_errors() { - type_error_struct!(self.tcx.sess, sp, ty, E0619, - "the type of this value must be known in this context") - .emit(); + self.need_type_info((**self).body_id, sp, ty); } self.demand_suptype(sp, self.tcx.types.err, ty); ty = self.tcx.types.err; diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index f59948e9fc42f..1c0e084832ebc 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4368,12 +4368,13 @@ i_am_a_function(); "##, E0619: r##" +#### Note: this error code is no longer emitted by the compiler. The type-checker needed to know the type of an expression, but that type had not yet been inferred. Erroneous code example: -```compile_fail,E0619 +```compile_fail let mut x = vec![]; match x.pop() { Some(v) => { diff --git a/src/test/compile-fail/epoch-raw-pointer-method-2015.rs b/src/test/compile-fail/epoch-raw-pointer-method-2015.rs index a71db040b50e7..6aa83a38b7ee9 100644 --- a/src/test/compile-fail/epoch-raw-pointer-method-2015.rs +++ b/src/test/compile-fail/epoch-raw-pointer-method-2015.rs @@ -18,6 +18,6 @@ fn main() { let x = 0; let y = &x as *const _; let _ = y.is_null(); - //~^ error: the type of this value must be known in this context [tyvar_behind_raw_pointer] + //~^ error: type annotations needed [tyvar_behind_raw_pointer] //~^^ warning: this was previously accepted } diff --git a/src/test/compile-fail/issue-15965.rs b/src/test/compile-fail/issue-15965.rs index 08b896f387bbe..76ba5a0f4b371 100644 --- a/src/test/compile-fail/issue-15965.rs +++ b/src/test/compile-fail/issue-15965.rs @@ -11,7 +11,7 @@ fn main() { return { return () } -//~^ ERROR the type of this value must be known in this context +//~^ ERROR type annotations needed [E0282] () ; } diff --git a/src/test/compile-fail/issue-2151.rs b/src/test/compile-fail/issue-2151.rs index fbd8f9163b5df..3cf971f3f8dfb 100644 --- a/src/test/compile-fail/issue-2151.rs +++ b/src/test/compile-fail/issue-2151.rs @@ -10,5 +10,5 @@ fn main() { let x = panic!(); - x.clone(); //~ ERROR the type of this value must be known in this context + x.clone(); //~ ERROR type annotations needed } diff --git a/src/test/compile-fail/match-vec-mismatch.rs b/src/test/compile-fail/match-vec-mismatch.rs index fed68da006889..998c11979953c 100644 --- a/src/test/compile-fail/match-vec-mismatch.rs +++ b/src/test/compile-fail/match-vec-mismatch.rs @@ -43,6 +43,6 @@ fn main() { fn another_fn_to_avoid_suppression() { match Default::default() { - [] => {} //~ ERROR the type of this value + [] => {} //~ ERROR type annotations needed }; } diff --git a/src/test/compile-fail/pat-tuple-bad-type.rs b/src/test/compile-fail/pat-tuple-bad-type.rs index fd4ab5d253158..251e7b47dcc99 100644 --- a/src/test/compile-fail/pat-tuple-bad-type.rs +++ b/src/test/compile-fail/pat-tuple-bad-type.rs @@ -12,7 +12,7 @@ fn main() { let x; match x { - (..) => {} //~ ERROR the type of this value must be known in this context + (..) => {} //~ ERROR type annotations needed _ => {} } diff --git a/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs index 12b48b2a6c8aa..8f5bf827fcf1a 100644 --- a/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs +++ b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs @@ -24,7 +24,7 @@ fn a() { match closure0.take() { Some(c) => { return c(); - //~^ ERROR the type of this value must be known in this context + //~^ ERROR type annotations needed } None => { } } diff --git a/src/test/ui/error-codes/E0619.rs b/src/test/ui/error-codes/E0619.rs deleted file mode 100644 index a5a5ff7218dcf..0000000000000 --- a/src/test/ui/error-codes/E0619.rs +++ /dev/null @@ -1,19 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn main() { - let x; - - match x { - (..) => {} //~ ERROR E0619 - _ => {} - } -} - diff --git a/src/test/ui/error-codes/E0619.stderr b/src/test/ui/error-codes/E0619.stderr deleted file mode 100644 index cec336cfcec66..0000000000000 --- a/src/test/ui/error-codes/E0619.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error[E0619]: the type of this value must be known in this context - --> $DIR/E0619.rs:15:9 - | -15 | (..) => {} //~ ERROR E0619 - | ^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/inference-variable-behind-raw-pointer.stderr b/src/test/ui/inference-variable-behind-raw-pointer.stderr index d0ee55c092b28..bb1d921f1c61c 100644 --- a/src/test/ui/inference-variable-behind-raw-pointer.stderr +++ b/src/test/ui/inference-variable-behind-raw-pointer.stderr @@ -1,4 +1,4 @@ -warning: the type of this value must be known in this context +warning: type annotations needed --> $DIR/inference-variable-behind-raw-pointer.rs:18:13 | 18 | if data.is_null() {} diff --git a/src/test/ui/span/issue-42234-unknown-receiver-type.rs b/src/test/ui/span/issue-42234-unknown-receiver-type.rs index d9cdd99c245e6..975c81955e0b3 100644 --- a/src/test/ui/span/issue-42234-unknown-receiver-type.rs +++ b/src/test/ui/span/issue-42234-unknown-receiver-type.rs @@ -15,11 +15,11 @@ fn shines_a_beacon_through_the_darkness() { let x: Option<_> = None; x.unwrap().method_that_could_exist_on_some_type(); - //~^ ERROR 17:5: 17:15: the type of this value must be known in this context + //~^ ERROR 17:5: 17:15: type annotations needed } fn courier_to_des_moines_and_points_west(data: &[u32]) -> String { - data.iter() //~ ERROR 22:5: 23:20: the type of this value must be known in this context + data.iter() //~ ERROR 22:5: 23:20: type annotations needed .sum::<_>() .to_string() } diff --git a/src/test/ui/span/issue-42234-unknown-receiver-type.stderr b/src/test/ui/span/issue-42234-unknown-receiver-type.stderr index ed756cdc553ce..2a85e1f1c45ba 100644 --- a/src/test/ui/span/issue-42234-unknown-receiver-type.stderr +++ b/src/test/ui/span/issue-42234-unknown-receiver-type.stderr @@ -1,13 +1,13 @@ -error[E0619]: the type of this value must be known in this context +error[E0282]: type annotations needed --> $DIR/issue-42234-unknown-receiver-type.rs:17:5 | 17 | x.unwrap().method_that_could_exist_on_some_type(); | ^^^^^^^^^^ -error[E0619]: the type of this value must be known in this context +error[E0282]: type annotations needed --> $DIR/issue-42234-unknown-receiver-type.rs:22:5 | -22 | / data.iter() //~ ERROR 22:5: 23:20: the type of this value must be known in this context +22 | / data.iter() //~ ERROR 22:5: 23:20: type annotations needed 23 | | .sum::<_>() | |___________________^ From d4b847504c2ecf32637486987ac299f91a3d1c54 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 13 Feb 2018 17:40:46 +0100 Subject: [PATCH 15/42] incr.comp.: Store DepNode colors in a dense array instead of a hashmap. --- src/librustc/dep_graph/graph.rs | 148 ++++++++++++++++++++++--------- src/librustc/dep_graph/prev.rs | 5 ++ src/librustc/ty/maps/plumbing.rs | 6 +- 3 files changed, 112 insertions(+), 47 deletions(-) diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 55ec8adb5fbf3..b77431e806a6d 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -74,7 +74,7 @@ struct DepGraphData { /// nodes and edges as well as all fingerprints of nodes that have them. previous: PreviousDepGraph, - colors: RefCell>, + colors: RefCell, /// When we load, there may be `.o` files, cached mir, or other such /// things available to us. If we find that they are not dirty, we @@ -97,8 +97,10 @@ impl DepGraph { // Pre-allocate the fingerprints array. We over-allocate a little so // that we hopefully don't have to re-allocate during this compilation // session. + let prev_graph_node_count = prev_graph.node_count(); + let fingerprints = IndexVec::from_elem_n(Fingerprint::ZERO, - (prev_graph.node_count() * 115) / 100); + (prev_graph_node_count * 115) / 100); DepGraph { data: Some(Rc::new(DepGraphData { previous_work_products: RefCell::new(FxHashMap()), @@ -106,7 +108,7 @@ impl DepGraph { dep_node_debug: RefCell::new(FxHashMap()), current: RefCell::new(CurrentDepGraph::new()), previous: prev_graph, - colors: RefCell::new(FxHashMap()), + colors: RefCell::new(DepNodeColorMap::new(prev_graph_node_count)), loaded_from_cache: RefCell::new(FxHashMap()), })), fingerprints: Rc::new(RefCell::new(fingerprints)), @@ -213,8 +215,6 @@ impl DepGraph { R: HashStable, { if let Some(ref data) = self.data { - debug_assert!(!data.colors.borrow().contains_key(&key)); - push(&data.current, key); if cfg!(debug_assertions) { profq_msg(ProfileQueriesMsg::TaskBegin(key.clone())) @@ -254,19 +254,21 @@ impl DepGraph { } // Determine the color of the new DepNode. - { - let prev_fingerprint = data.previous.fingerprint_of(&key); + if let Some(prev_index) = data.previous.node_to_index_opt(&key) { + let prev_fingerprint = data.previous.fingerprint_by_index(prev_index); - let color = if Some(current_fingerprint) == prev_fingerprint { + let color = if current_fingerprint == prev_fingerprint { DepNodeColor::Green(dep_node_index) } else { DepNodeColor::Red }; - let old_value = data.colors.borrow_mut().insert(key, color); - debug_assert!(old_value.is_none(), + let mut colors = data.colors.borrow_mut(); + debug_assert!(colors.get(prev_index).is_none(), "DepGraph::with_task() - Duplicate DepNodeColor \ insertion for {:?}", key); + + colors.insert(prev_index, color); } (result, dep_node_index) @@ -281,9 +283,11 @@ impl DepGraph { let mut fingerprints = self.fingerprints.borrow_mut(); let dep_node_index = DepNodeIndex::new(fingerprints.len()); fingerprints.push(fingerprint); + debug_assert!(fingerprints[dep_node_index] == fingerprint, "DepGraph::with_task() - Assigned fingerprint to \ unexpected index for {:?}", key); + (result, dep_node_index) } else { (task(cx, arg), DepNodeIndex::INVALID) @@ -356,6 +360,15 @@ impl DepGraph { .unwrap() } + #[inline] + pub fn dep_node_exists(&self, dep_node: &DepNode) -> bool { + if let Some(ref data) = self.data { + data.current.borrow_mut().node_to_node_index.contains_key(dep_node) + } else { + false + } + } + #[inline] pub fn fingerprint_of(&self, dep_node_index: DepNodeIndex) -> Fingerprint { match self.fingerprints.borrow().get(dep_node_index) { @@ -495,7 +508,17 @@ impl DepGraph { } pub fn node_color(&self, dep_node: &DepNode) -> Option { - self.data.as_ref().and_then(|data| data.colors.borrow().get(dep_node).cloned()) + if let Some(ref data) = self.data { + if let Some(prev_index) = data.previous.node_to_index_opt(dep_node) { + return data.colors.borrow().get(prev_index) + } else { + // This is a node that did not exist in the previous compilation + // session, so we consider it to be red. + return Some(DepNodeColor::Red) + } + } + + None } pub fn try_mark_green<'tcx>(&self, @@ -505,7 +528,6 @@ impl DepGraph { debug!("try_mark_green({:?}) - BEGIN", dep_node); let data = self.data.as_ref().unwrap(); - debug_assert!(!data.colors.borrow().contains_key(dep_node)); debug_assert!(!data.current.borrow().node_to_node_index.contains_key(dep_node)); if dep_node.kind.is_input() { @@ -535,19 +557,22 @@ impl DepGraph { } }; + debug_assert!(data.colors.borrow().get(prev_dep_node_index).is_none()); + let mut current_deps = Vec::new(); for &dep_dep_node_index in prev_deps { - let dep_dep_node = &data.previous.index_to_node(dep_dep_node_index); + let dep_dep_node_color = data.colors.borrow().get(dep_dep_node_index); - let dep_dep_node_color = data.colors.borrow().get(dep_dep_node).cloned(); match dep_dep_node_color { Some(DepNodeColor::Green(node_index)) => { // This dependency has been marked as green before, we are // still fine and can continue with checking the other // dependencies. debug!("try_mark_green({:?}) --- found dependency {:?} to \ - be immediately green", dep_node, dep_dep_node); + be immediately green", + dep_node, + data.previous.index_to_node(dep_dep_node_index)); current_deps.push(node_index); } Some(DepNodeColor::Red) => { @@ -556,10 +581,14 @@ impl DepGraph { // mark the DepNode as green and also don't need to bother // with checking any of the other dependencies. debug!("try_mark_green({:?}) - END - dependency {:?} was \ - immediately red", dep_node, dep_dep_node); + immediately red", + dep_node, + data.previous.index_to_node(dep_dep_node_index)); return None } None => { + let dep_dep_node = &data.previous.index_to_node(dep_dep_node_index); + // We don't know the state of this dependency. If it isn't // an input node, let's try to mark it green recursively. if !dep_dep_node.kind.is_input() { @@ -601,10 +630,8 @@ impl DepGraph { debug!("try_mark_green({:?}) --- trying to force \ dependency {:?}", dep_node, dep_dep_node); if ::ty::maps::force_from_dep_node(tcx, dep_dep_node) { - let dep_dep_node_color = data.colors - .borrow() - .get(dep_dep_node) - .cloned(); + let dep_dep_node_color = data.colors.borrow().get(dep_dep_node_index); + match dep_dep_node_color { Some(DepNodeColor::Green(node_index)) => { debug!("try_mark_green({:?}) --- managed to \ @@ -681,26 +708,21 @@ impl DepGraph { } // ... and finally storing a "Green" entry in the color map. - let old_color = data.colors - .borrow_mut() - .insert(*dep_node, DepNodeColor::Green(dep_node_index)); - debug_assert!(old_color.is_none(), + let mut colors = data.colors.borrow_mut(); + debug_assert!(colors.get(prev_dep_node_index).is_none(), "DepGraph::try_mark_green() - Duplicate DepNodeColor \ insertion for {:?}", dep_node); + colors.insert(prev_dep_node_index, DepNodeColor::Green(dep_node_index)); + debug!("try_mark_green({:?}) - END - successfully marked as green", dep_node); Some(dep_node_index) } - // Used in various assertions - pub fn is_green(&self, dep_node_index: DepNodeIndex) -> bool { - let dep_node = self.data.as_ref().unwrap().current.borrow().nodes[dep_node_index]; - self.data.as_ref().unwrap().colors.borrow().get(&dep_node).map(|&color| { - match color { - DepNodeColor::Red => false, - DepNodeColor::Green(_) => true, - } - }).unwrap_or(false) + // Returns true if the given node has been marked as green during the + // current compilation session. Used in various assertions + pub fn is_green(&self, dep_node: &DepNode) -> bool { + self.node_color(dep_node).map(|c| c.is_green()).unwrap_or(false) } // This method loads all on-disk cacheable query results into memory, so @@ -714,20 +736,25 @@ impl DepGraph { pub fn exec_cache_promotions<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) { let green_nodes: Vec = { let data = self.data.as_ref().unwrap(); - data.colors.borrow().iter().filter_map(|(dep_node, color)| match color { - DepNodeColor::Green(_) => { - if dep_node.cache_on_disk(tcx) { - Some(*dep_node) - } else { + let colors = data.colors.borrow(); + colors.values.indices().filter_map(|prev_index| { + match colors.get(prev_index) { + Some(DepNodeColor::Green(_)) => { + let dep_node = data.previous.index_to_node(prev_index); + if dep_node.cache_on_disk(tcx) { + Some(dep_node) + } else { + None + } + } + None | + Some(DepNodeColor::Red) => { + // We can skip red nodes because a node can only be marked + // as red if the query result was recomputed and thus is + // already in memory. None } } - DepNodeColor::Red => { - // We can skip red nodes because a node can only be marked - // as red if the query result was recomputed and thus is - // already in memory. - None - } }).collect() }; @@ -1052,3 +1079,36 @@ enum OpenTask { node: DepNode, }, } + +// A data structure that stores Option values as a contiguous +// array, using one u32 per entry. +struct DepNodeColorMap { + values: IndexVec, +} + +const COMPRESSED_NONE: u32 = 0; +const COMPRESSED_RED: u32 = 1; +const COMPRESSED_FIRST_GREEN: u32 = 2; + +impl DepNodeColorMap { + fn new(size: usize) -> DepNodeColorMap { + DepNodeColorMap { + values: IndexVec::from_elem_n(COMPRESSED_NONE, size) + } + } + + fn get(&self, index: SerializedDepNodeIndex) -> Option { + match self.values[index] { + COMPRESSED_NONE => None, + COMPRESSED_RED => Some(DepNodeColor::Red), + value => Some(DepNodeColor::Green(DepNodeIndex(value - COMPRESSED_FIRST_GREEN))) + } + } + + fn insert(&mut self, index: SerializedDepNodeIndex, color: DepNodeColor) { + self.values[index] = match color { + DepNodeColor::Red => COMPRESSED_RED, + DepNodeColor::Green(index) => index.0 + COMPRESSED_FIRST_GREEN, + } + } +} diff --git a/src/librustc/dep_graph/prev.rs b/src/librustc/dep_graph/prev.rs index 50e1ee88a4614..504b60e763e23 100644 --- a/src/librustc/dep_graph/prev.rs +++ b/src/librustc/dep_graph/prev.rs @@ -49,6 +49,11 @@ impl PreviousDepGraph { self.index[dep_node] } + #[inline] + pub fn node_to_index_opt(&self, dep_node: &DepNode) -> Option { + self.index.get(dep_node).cloned() + } + #[inline] pub fn fingerprint_of(&self, dep_node: &DepNode) -> Option { self.index diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 0ab6ee1a54a9b..956348edcce65 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -147,7 +147,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } match self.dep_graph.try_mark_green(self.global_tcx(), &dep_node) { Some(dep_node_index) => { - debug_assert!(self.dep_graph.is_green(dep_node_index)); + debug_assert!(self.dep_graph.is_green(&dep_node)); self.dep_graph.read_index(dep_node_index); Some(dep_node_index) } @@ -390,7 +390,7 @@ macro_rules! define_maps { dep_node: &DepNode) -> Result<$V, CycleError<'a, $tcx>> { - debug_assert!(tcx.dep_graph.is_green(dep_node_index)); + debug_assert!(tcx.dep_graph.is_green(dep_node)); // First we try to load the result from the on-disk cache let result = if Self::cache_on_disk(key) && @@ -478,7 +478,7 @@ macro_rules! define_maps { span: Span, dep_node: DepNode) -> Result<($V, DepNodeIndex), CycleError<'a, $tcx>> { - debug_assert!(tcx.dep_graph.node_color(&dep_node).is_none()); + debug_assert!(!tcx.dep_graph.dep_node_exists(&dep_node)); profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin); let res = tcx.cycle_check(span, Query::$name(key), || { From 9e9c55f8fd064335cadc28bd9a0152538dcc9fa0 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 14 Feb 2018 10:22:12 -0800 Subject: [PATCH 16/42] Update E0657 stderr to match changed test --- src/test/ui/error-codes/E0657.stderr | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/ui/error-codes/E0657.stderr b/src/test/ui/error-codes/E0657.stderr index d3b53d37a30a0..e039d645fa6db 100644 --- a/src/test/ui/error-codes/E0657.stderr +++ b/src/test/ui/error-codes/E0657.stderr @@ -1,14 +1,14 @@ error[E0657]: `impl Trait` can only capture lifetimes bound at the fn or impl level - --> $DIR/E0657.rs:20:32 + --> $DIR/E0657.rs:20:31 | -20 | -> impl for<'a> Id> - | ^^ +20 | -> Box Id>> + | ^^ error[E0657]: `impl Trait` can only capture lifetimes bound at the fn or impl level - --> $DIR/E0657.rs:29:36 + --> $DIR/E0657.rs:29:35 | -29 | -> impl for<'a> Id> - | ^^ +29 | -> Box Id>> + | ^^ error: aborting due to 2 previous errors From 7062955ad9186c3a8cd785ecf90bc10f1949b16a Mon Sep 17 00:00:00 2001 From: bobtwinkles Date: Thu, 15 Feb 2018 01:25:29 -0500 Subject: [PATCH 17/42] Fix arguments specified by lxl in two-phase-bin-ops test --- src/test/run-pass/borrowck/two-phase-bin-ops.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/run-pass/borrowck/two-phase-bin-ops.rs b/src/test/run-pass/borrowck/two-phase-bin-ops.rs index 9bded41e1f933..1b2529d7875ab 100644 --- a/src/test/run-pass/borrowck/two-phase-bin-ops.rs +++ b/src/test/run-pass/borrowck/two-phase-bin-ops.rs @@ -9,7 +9,6 @@ // except according to those terms. // revisions: lxl nll -//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows #![cfg_attr(nll, feature(nll))] From f787bddcff700a139a0acdc815b4e5b0d641fad9 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 14 Feb 2018 16:11:02 +0100 Subject: [PATCH 18/42] Turn features() into a query. --- src/librustc/dep_graph/dep_node.rs | 10 +-- src/librustc/hir/lowering.rs | 6 +- src/librustc/ich/impls_syntax.rs | 19 ++++++ src/librustc/infer/error_reporting/mod.rs | 4 +- src/librustc/middle/stability.rs | 14 ++-- src/librustc/session/mod.rs | 66 ++++++------------- src/librustc/traits/specialize/mod.rs | 2 +- src/librustc/ty/context.rs | 64 ++++++++++++++++-- src/librustc/ty/maps/config.rs | 6 ++ src/librustc/ty/maps/mod.rs | 8 +++ src/librustc/ty/maps/plumbing.rs | 1 + src/librustc/ty/mod.rs | 2 +- src/librustc_borrowck/borrowck/mod.rs | 2 +- src/librustc_const_eval/_match.rs | 4 +- src/librustc_const_eval/check_match.rs | 2 +- src/librustc_const_eval/eval.rs | 2 +- src/librustc_driver/driver.rs | 10 +-- src/librustc_incremental/assert_dep_graph.rs | 2 +- .../persist/dirty_clean.rs | 2 +- src/librustc_lint/builtin.rs | 2 +- src/librustc_lint/unused.rs | 2 +- src/librustc_metadata/native_libs.rs | 4 +- src/librustc_mir/borrow_check/mod.rs | 10 +-- src/librustc_mir/build/cfg.rs | 2 +- src/librustc_mir/build/matches/simplify.rs | 2 +- .../transform/clean_end_regions.rs | 2 +- src/librustc_mir/transform/qualify_consts.rs | 2 +- src/librustc_mir/util/borrowck_errors.rs | 2 +- src/librustc_plugin/load.rs | 2 +- src/librustc_resolve/build_reduced_graph.rs | 2 +- src/librustc_resolve/lib.rs | 6 +- src/librustc_resolve/macros.rs | 4 +- src/librustc_resolve/resolve_imports.rs | 2 +- src/librustc_trans_utils/symbol_names_test.rs | 2 +- src/librustc_typeck/astconv.rs | 2 +- src/librustc_typeck/check/_match.rs | 2 +- src/librustc_typeck/check/coercion.rs | 2 +- src/librustc_typeck/check/method/probe.rs | 2 +- src/librustc_typeck/check/mod.rs | 4 +- src/librustc_typeck/check/wfcheck.rs | 2 +- src/librustc_typeck/coherence/mod.rs | 2 +- src/librustc_typeck/collect.rs | 6 +- src/librustc_typeck/lib.rs | 2 +- src/librustdoc/test.rs | 2 +- src/libsyntax/ext/tt/macro_rules.rs | 17 +++-- src/libsyntax/ext/tt/quoted.rs | 15 ++--- src/libsyntax/feature_gate.rs | 7 ++ 47 files changed, 202 insertions(+), 135 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 4034055d04155..d1734e214d561 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -436,6 +436,9 @@ impl DepKind { } define_dep_nodes!( <'tcx> + // We use this for most things when incr. comp. is turned off. + [] Null, + // Represents the `Krate` as a whole (the `hir::Krate` value) (as // distinct from the krate module). This is basically a hash of // the entire krate, so if you read from `Krate` (e.g., by calling @@ -605,8 +608,8 @@ define_dep_nodes!( <'tcx> [input] MissingExternCrateItem(CrateNum), [input] UsedCrateSource(CrateNum), [input] PostorderCnums, - [input] HasCloneClosures(CrateNum), - [input] HasCopyClosures(CrateNum), + [] HasCloneClosures(CrateNum), + [] HasCopyClosures(CrateNum), // This query is not expected to have inputs -- as a result, it's // not a good candidate for "replay" because it's essentially a @@ -630,8 +633,6 @@ define_dep_nodes!( <'tcx> [] CompileCodegenUnit(InternedString), [input] OutputFilenames, [anon] NormalizeTy, - // We use this for most things when incr. comp. is turned off. - [] Null, [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, @@ -642,6 +643,7 @@ define_dep_nodes!( <'tcx> [] GetSymbolExportLevel(DefId), + [input] Features, ); trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug { diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 55dcb16c3c95f..974ee99efabbe 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -550,7 +550,7 @@ impl<'a> LoweringContext<'a> { { assert!(!self.is_collecting_in_band_lifetimes); assert!(self.lifetimes_to_define.is_empty()); - self.is_collecting_in_band_lifetimes = self.sess.features.borrow().in_band_lifetimes; + self.is_collecting_in_band_lifetimes = self.sess.features_untracked().in_band_lifetimes; assert!(self.in_band_ty_params.is_empty()); @@ -957,7 +957,7 @@ impl<'a> LoweringContext<'a> { let span = t.span; match itctx { ImplTraitContext::Existential => { - let has_feature = self.sess.features.borrow().conservative_impl_trait; + let has_feature = self.sess.features_untracked().conservative_impl_trait; if !t.span.allows_unstable() && !has_feature { emit_feature_err(&self.sess.parse_sess, "conservative_impl_trait", t.span, GateIssue::Language, @@ -981,7 +981,7 @@ impl<'a> LoweringContext<'a> { }, lifetimes) }, ImplTraitContext::Universal(def_id) => { - let has_feature = self.sess.features.borrow().universal_impl_trait; + let has_feature = self.sess.features_untracked().universal_impl_trait; if !t.span.allows_unstable() && !has_feature { emit_feature_err(&self.sess.parse_sess, "universal_impl_trait", t.span, GateIssue::Language, diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index c31a5c9d86d77..f935cbfcde992 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -17,6 +17,7 @@ use std::hash as std_hash; use std::mem; use syntax::ast; +use syntax::feature_gate; use syntax::parse::token; use syntax::symbol::InternedString; use syntax::tokenstream; @@ -460,3 +461,21 @@ fn stable_non_narrow_char(swc: ::syntax_pos::NonNarrowChar, (pos.0 - filemap_start.0, width as u32) } + + + +impl<'gcx> HashStable> for feature_gate::Features { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + // Unfortunately we cannot exhaustively list fields here, since the + // struct is macro generated. + self.declared_stable_lang_features.hash_stable(hcx, hasher); + self.declared_lib_features.hash_stable(hcx, hasher); + + self.walk_feature_fields(|feature_name, value| { + feature_name.hash_stable(hcx, hasher); + value.hash_stable(hcx, hasher); + }); + } +} diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 03fc40b2e39fc..ae740707da5d6 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -262,11 +262,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { will_later_be_reported_by_nll: bool) { debug!("report_region_errors(): {} errors to start", errors.len()); - if will_later_be_reported_by_nll && self.tcx.sess.nll() { + if will_later_be_reported_by_nll && self.tcx.nll() { // With `#![feature(nll)]`, we want to present a nice user // experience, so don't even mention the errors from the // AST checker. - if self.tcx.sess.features.borrow().nll { + if self.tcx.features().nll { return; } diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index e80ea16f565ab..16c33d6bd837d 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -131,7 +131,7 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { item_sp: Span, kind: AnnotationKind, visit_children: F) where F: FnOnce(&mut Self) { - if self.tcx.sess.features.borrow().staged_api { + if self.tcx.features().staged_api { // This crate explicitly wants staged API. debug!("annotate(id = {:?}, attrs = {:?})", id, attrs); if let Some(..) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) { @@ -398,7 +398,7 @@ impl<'a, 'tcx> Index<'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Index<'tcx> { let is_staged_api = tcx.sess.opts.debugging_opts.force_unstable_if_unmarked || - tcx.sess.features.borrow().staged_api; + tcx.features().staged_api; let mut staged_api = FxHashMap(); staged_api.insert(LOCAL_CRATE, is_staged_api); let mut index = Index { @@ -408,7 +408,7 @@ impl<'a, 'tcx> Index<'tcx> { active_features: FxHashSet(), }; - let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features; + let ref active_lib_features = tcx.features().declared_lib_features; // Put the active features into a map for quick lookup index.active_features = active_lib_features.iter().map(|&(ref s, _)| s.clone()).collect(); @@ -677,7 +677,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { // There's no good place to insert stability check for non-Copy unions, // so semi-randomly perform it here in stability.rs - hir::ItemUnion(..) if !self.tcx.sess.features.borrow().untagged_unions => { + hir::ItemUnion(..) if !self.tcx.features().untagged_unions => { let def_id = self.tcx.hir.local_def_id(item.id); let adt_def = self.tcx.adt_def(def_id); let ty = self.tcx.type_of(def_id); @@ -721,8 +721,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// were expected to be library features), and the list of features used from /// libraries, identify activated features that don't exist and error about them. pub fn check_unused_or_stable_features<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - let sess = &tcx.sess; - let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); if tcx.stability().staged_api[&LOCAL_CRATE] { @@ -736,12 +734,12 @@ pub fn check_unused_or_stable_features<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { krate.visit_all_item_likes(&mut missing.as_deep_visitor()); } - let ref declared_lib_features = sess.features.borrow().declared_lib_features; + let ref declared_lib_features = tcx.features().declared_lib_features; let mut remaining_lib_features: FxHashMap = declared_lib_features.clone().into_iter().collect(); remaining_lib_features.remove(&Symbol::intern("proc_macro")); - for &(ref stable_lang_feature, span) in &sess.features.borrow().declared_stable_lang_features { + for &(ref stable_lang_feature, span) in &tcx.features().declared_stable_lang_features { let version = find_lang_feature_accepted_version(&stable_lang_feature.as_str()) .expect("unexpectedly couldn't find version feature was stabilized"); tcx.lint_node(lint::builtin::STABLE_FEATURES, diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 9d7a9acc3d533..bcffce80911f7 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -19,7 +19,7 @@ use lint; use middle::allocator::AllocatorKind; use middle::dependency_format; use session::search_paths::PathKind; -use session::config::{BorrowckMode, DebugInfoLevel, OutputType, Epoch}; +use session::config::{DebugInfoLevel, OutputType, Epoch}; use ty::tls; use util::nodemap::{FxHashMap, FxHashSet}; use util::common::{duration_to_secs_str, ErrorReported}; @@ -91,7 +91,8 @@ pub struct Session { /// multiple crates with the same name to coexist. See the /// trans::back::symbol_names module for more information. pub crate_disambiguator: RefCell>, - pub features: RefCell, + + features: RefCell>, /// The maximum recursion limit for potentially infinitely recursive /// operations such as auto-dereference and monomorphization. @@ -192,6 +193,7 @@ impl Session { None => bug!("accessing disambiguator before initialization"), } } + pub fn struct_span_warn<'a, S: Into>(&'a self, sp: S, msg: &str) @@ -443,16 +445,22 @@ impl Session { self.opts.debugging_opts.print_llvm_passes } - /// If true, we should use NLL-style region checking instead of - /// lexical style. - pub fn nll(&self) -> bool { - self.features.borrow().nll || self.opts.debugging_opts.nll + /// Get the features enabled for the current compilation session. Do not use + /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents + /// dependency tracking. Use tcx.features() instead. + #[inline] + pub fn features_untracked(&self) -> cell::Ref { + let features = self.features.borrow(); + + if features.is_none() { + bug!("Access to Session::features before it is initialized"); + } + + cell::Ref::map(features, |r| r.as_ref().unwrap()) } - /// If true, we should use the MIR-based borrowck (we may *also* use - /// the AST-based borrowck). - pub fn use_mir(&self) -> bool { - self.borrowck_mode().use_mir() + pub fn init_features(&self, features: feature_gate::Features) { + *(self.features.borrow_mut()) = Some(features); } /// If true, we should gather causal information during NLL @@ -462,42 +470,6 @@ impl Session { self.opts.debugging_opts.nll_dump_cause } - /// If true, we should enable two-phase borrows checks. This is - /// done with either `-Ztwo-phase-borrows` or with - /// `#![feature(nll)]`. - pub fn two_phase_borrows(&self) -> bool { - self.features.borrow().nll || self.opts.debugging_opts.two_phase_borrows - } - - /// What mode(s) of borrowck should we run? AST? MIR? both? - /// (Also considers the `#![feature(nll)]` setting.) - pub fn borrowck_mode(&self) -> BorrowckMode { - match self.opts.borrowck_mode { - mode @ BorrowckMode::Mir | - mode @ BorrowckMode::Compare => mode, - - mode @ BorrowckMode::Ast => { - if self.nll() { - BorrowckMode::Mir - } else { - mode - } - } - - } - } - - /// Should we emit EndRegion MIR statements? These are consumed by - /// MIR borrowck, but not when NLL is used. They are also consumed - /// by the validation stuff. - pub fn emit_end_regions(&self) -> bool { - // FIXME(#46875) -- we should not emit end regions when NLL is enabled, - // but for now we can't stop doing so because it causes false positives - self.opts.debugging_opts.emit_end_regions || - self.opts.debugging_opts.mir_emit_validate > 0 || - self.use_mir() - } - /// Calculates the flavor of LTO to use for this compilation. pub fn lto(&self) -> config::Lto { // If our target has codegen requirements ignore the command line @@ -1009,7 +981,7 @@ pub fn build_session_(sopts: config::Options, crate_types: RefCell::new(Vec::new()), dependency_formats: RefCell::new(FxHashMap()), crate_disambiguator: RefCell::new(None), - features: RefCell::new(feature_gate::Features::new()), + features: RefCell::new(None), recursion_limit: Cell::new(64), type_length_limit: Cell::new(1048576), next_node_id: Cell::new(NodeId::new(1)), diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index afe29cc0e7baf..1d0b40f44c05d 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -162,7 +162,7 @@ pub(super) fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // The feature gate should prevent introducing new specializations, but not // taking advantage of upstream ones. - if !tcx.sess.features.borrow().specialization && + if !tcx.features().specialization && (impl1_def_id.is_local() || impl2_def_id.is_local()) { return false; } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index e4e07454c97ac..d01bfcefbb7f1 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -14,7 +14,7 @@ use dep_graph::DepGraph; use dep_graph::{DepNode, DepConstructor}; use errors::DiagnosticBuilder; use session::Session; -use session::config::OutputFilenames; +use session::config::{BorrowckMode, OutputFilenames}; use middle; use hir::{TraitCandidate, HirId, ItemLocalId}; use hir::def::{Def, Export}; @@ -71,6 +71,7 @@ use syntax::abi; use syntax::ast::{self, Name, NodeId}; use syntax::attr; use syntax::codemap::MultiSpan; +use syntax::feature_gate; use syntax::symbol::{Symbol, keywords}; use syntax_pos::Span; @@ -1251,6 +1252,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.stability_index(LOCAL_CRATE) } + pub fn features(self) -> Rc { + self.features_query(LOCAL_CRATE) + } + pub fn crates(self) -> Rc> { self.all_crate_nums(LOCAL_CRATE) } @@ -1362,6 +1367,53 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.on_disk_query_result_cache.serialize(self.global_tcx(), encoder) } + /// If true, we should use NLL-style region checking instead of + /// lexical style. + pub fn nll(self) -> bool { + self.features().nll || self.sess.opts.debugging_opts.nll + } + + /// If true, we should use the MIR-based borrowck (we may *also* use + /// the AST-based borrowck). + pub fn use_mir(self) -> bool { + self.borrowck_mode().use_mir() + } + + /// If true, we should enable two-phase borrows checks. This is + /// done with either `-Ztwo-phase-borrows` or with + /// `#![feature(nll)]`. + pub fn two_phase_borrows(self) -> bool { + self.features().nll || self.sess.opts.debugging_opts.two_phase_borrows + } + + /// What mode(s) of borrowck should we run? AST? MIR? both? + /// (Also considers the `#![feature(nll)]` setting.) + pub fn borrowck_mode(&self) -> BorrowckMode { + match self.sess.opts.borrowck_mode { + mode @ BorrowckMode::Mir | + mode @ BorrowckMode::Compare => mode, + + mode @ BorrowckMode::Ast => { + if self.nll() { + BorrowckMode::Mir + } else { + mode + } + } + + } + } + + /// Should we emit EndRegion MIR statements? These are consumed by + /// MIR borrowck, but not when NLL is used. They are also consumed + /// by the validation stuff. + pub fn emit_end_regions(self) -> bool { + // FIXME(#46875) -- we should not emit end regions when NLL is enabled, + // but for now we can't stop doing so because it causes false positives + self.sess.opts.debugging_opts.emit_end_regions || + self.sess.opts.debugging_opts.mir_emit_validate > 0 || + self.use_mir() + } } impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { @@ -2020,7 +2072,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } pub fn mk_diverging_default(self) -> Ty<'tcx> { - if self.sess.features.borrow().never_type { + if self.features().never_type { self.types.never } else { self.intern_tup(&[], true) @@ -2395,13 +2447,17 @@ pub fn provide(providers: &mut ty::maps::Providers) { }; providers.has_copy_closures = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - tcx.sess.features.borrow().copy_closures + tcx.features().copy_closures }; providers.has_clone_closures = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - tcx.sess.features.borrow().clone_closures + tcx.features().clone_closures }; providers.fully_normalize_monormophic_ty = |tcx, ty| { tcx.fully_normalize_associated_types_in(&ty) }; + providers.features_query = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + Rc::new(tcx.sess.features_untracked().clone()) + }; } diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index eb07876b05f26..c91b30440e5e3 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -593,6 +593,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::fully_normalize_monormophic_ty<'t } } +impl<'tcx> QueryDescription<'tcx> for queries::features_query<'tcx> { + fn describe(_tcx: TyCtxt, _: CrateNum) -> String { + format!("looking up enabled feature gates") + } +} + impl<'tcx> QueryDescription<'tcx> for queries::typeck_tables_of<'tcx> { #[inline] fn cache_on_disk(def_id: Self::Key) -> bool { diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 85fca68187fe6..887b51113b502 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -52,6 +52,7 @@ use syntax_pos::{Span, DUMMY_SP}; use syntax_pos::symbol::InternedString; use syntax::attr; use syntax::ast; +use syntax::feature_gate; use syntax::symbol::Symbol; #[macro_use] @@ -369,12 +370,19 @@ define_maps! { <'tcx> // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. [] fn instance_def_size_estimate: instance_def_size_estimate_dep_node(ty::InstanceDef<'tcx>) -> usize, + + [] fn features_query: features_node(CrateNum) -> Rc, } ////////////////////////////////////////////////////////////////////// // These functions are little shims used to find the dep-node for a // given query when there is not a *direct* mapping: + +fn features_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { + DepConstructor::Features +} + fn erase_regions_ty<'tcx>(ty: Ty<'tcx>) -> DepConstructor<'tcx> { DepConstructor::EraseRegionsTy { ty } } diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 0ab6ee1a54a9b..ffc51c28f0b63 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -923,6 +923,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::TargetFeaturesEnabled => { force!(target_features_enabled, def_id!()); } DepKind::GetSymbolExportLevel => { force!(symbol_export_level, def_id!()); } + DepKind::Features => { force!(features_query, LOCAL_CRATE); } } true diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index f52f2ea0f9fc8..07d886edb2059 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2270,7 +2270,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Returns true if the impls are the same polarity and are implementing /// a trait which contains no items pub fn impls_are_allowed_to_overlap(self, def_id1: DefId, def_id2: DefId) -> bool { - if !self.sess.features.borrow().overlapping_marker_traits { + if !self.features().overlapping_marker_traits { return false; } let trait1_is_empty = self.impl_trait_ref(def_id1) diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 738c0d82ee1b5..d07686960d0b7 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -275,7 +275,7 @@ impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> { o: Origin) -> DiagnosticBuilder<'a> { - if !o.should_emit_errors(self.tcx.sess.borrowck_mode()) { + if !o.should_emit_errors(self.tcx.borrowck_mode()) { self.tcx.sess.diagnostic().cancel(&mut diag); } diag diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index a7c382eba5091..4db460f43c508 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -201,7 +201,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { - if self.tcx.sess.features.borrow().never_type { + if self.tcx.features().never_type { self.tcx.is_ty_uninhabited_from(self.module, ty) } else { false @@ -227,7 +227,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { substs: &'tcx ty::subst::Substs<'tcx>) -> bool { - if self.tcx.sess.features.borrow().never_type { + if self.tcx.features().never_type { self.tcx.is_enum_variant_uninhabited_from(self.module, variant, substs) } else { false diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index ae53ed0e1140d..6f7143c185cb3 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -212,7 +212,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let pat_ty = self.tables.node_id_to_type(scrut.hir_id); let module = self.tcx.hir.get_module_parent(scrut.id); if inlined_arms.is_empty() { - let scrutinee_is_uninhabited = if self.tcx.sess.features.borrow().never_type { + let scrutinee_is_uninhabited = if self.tcx.features().never_type { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { self.conservative_is_uninhabited(pat_ty) diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 8e4ec93c14bae..2a571fa82643b 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -398,7 +398,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, }).collect::, _>>()?)))) } hir::ExprIndex(ref arr, ref idx) => { - if !tcx.sess.features.borrow().const_indexing { + if !tcx.features().const_indexing { signal!(e, IndexOpFeatureGated); } let arr = cx.eval(arr)?; diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index f344624666a6c..9d358c439db6b 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -648,7 +648,7 @@ pub fn phase_2_configure_and_expand_inner<'a, F>(sess: &'a Session, let (mut krate, features) = syntax::config::features(krate, &sess.parse_sess, sess.opts.test); // these need to be set "early" so that expansion sees `quote` if enabled. - *sess.features.borrow_mut() = features; + sess.init_features(features); *sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs); @@ -688,7 +688,7 @@ pub fn phase_2_configure_and_expand_inner<'a, F>(sess: &'a Session, let mut registry = registry.unwrap_or(Registry::new(sess, krate.span)); time(time_passes, "plugin registration", || { - if sess.features.borrow().rustc_diagnostic_macros { + if sess.features_untracked().rustc_diagnostic_macros { registry.register_macro("__diagnostic_used", diagnostics::plugin::expand_diagnostic_used); registry.register_macro("__register_diagnostic", @@ -738,7 +738,7 @@ pub fn phase_2_configure_and_expand_inner<'a, F>(sess: &'a Session, crate_loader, &resolver_arenas); resolver.whitelisted_legacy_custom_derives = whitelisted_legacy_custom_derives; - syntax_ext::register_builtins(&mut resolver, syntax_exts, sess.features.borrow().quote); + syntax_ext::register_builtins(&mut resolver, syntax_exts, sess.features_untracked().quote); krate = time(time_passes, "expansion", || { // Windows dlls do not have rpaths, so they don't know how to find their @@ -769,7 +769,7 @@ pub fn phase_2_configure_and_expand_inner<'a, F>(sess: &'a Session, .filter(|p| env::join_paths(iter::once(p)).is_ok())) .unwrap()); } - let features = sess.features.borrow(); + let features = sess.features_untracked(); let cfg = syntax::ext::expand::ExpansionConfig { features: Some(&features), recursion_limit: sess.recursion_limit.get(), @@ -874,7 +874,7 @@ pub fn phase_2_configure_and_expand_inner<'a, F>(sess: &'a Session, sess.track_errors(|| { syntax::feature_gate::check_crate(&krate, &sess.parse_sess, - &sess.features.borrow(), + &sess.features_untracked(), &attributes, sess.opts.unstable_features); }) diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs index 5976b80d90f87..17a6176b79e95 100644 --- a/src/librustc_incremental/assert_dep_graph.rs +++ b/src/librustc_incremental/assert_dep_graph.rs @@ -69,7 +69,7 @@ pub fn assert_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { // if the `rustc_attrs` feature is not enabled, then the // attributes we are interested in cannot be present anyway, so // skip the walk. - if !tcx.sess.features.borrow().rustc_attrs { + if !tcx.features().rustc_attrs { return; } diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index c3e283535ec82..e114606a63115 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -219,7 +219,7 @@ impl Assertion { pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { // can't add `#[rustc_dirty]` etc without opting in to this feature - if !tcx.sess.features.borrow().rustc_attrs { + if !tcx.features().rustc_attrs { return; } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index de55710bdf3d0..ea136475dba4f 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1355,7 +1355,7 @@ impl UnreachablePub { // visibility is token at start of declaration (can be macro // variable rather than literal `pub`) let pub_span = cx.tcx.sess.codemap().span_until_char(def_span, ' '); - let replacement = if cx.tcx.sess.features.borrow().crate_visibility_modifier { + let replacement = if cx.tcx.features().crate_visibility_modifier { "crate" } else { "pub(crate)" diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 439533fae49d9..6ab3172c4fefa 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -72,7 +72,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { let mut fn_warned = false; let mut op_warned = false; - if cx.tcx.sess.features.borrow().fn_must_use { + if cx.tcx.features().fn_must_use { let maybe_def = match expr.node { hir::ExprCall(ref callee, _) => { match callee.node { diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs index c0ce32cc97068..2504f8dc251f9 100644 --- a/src/librustc_metadata/native_libs.rs +++ b/src/librustc_metadata/native_libs.rs @@ -146,7 +146,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { None => self.tcx.sess.err(msg), } } - if lib.cfg.is_some() && !self.tcx.sess.features.borrow().link_cfg { + if lib.cfg.is_some() && !self.tcx.features().link_cfg { feature_gate::emit_feature_err(&self.tcx.sess.parse_sess, "link_cfg", span.unwrap(), @@ -154,7 +154,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { "is feature gated"); } if lib.kind == cstore::NativeStaticNobundle && - !self.tcx.sess.features.borrow().static_nobundle { + !self.tcx.features().static_nobundle { feature_gate::emit_feature_err(&self.tcx.sess.parse_sess, "static_nobundle", span.unwrap(), diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index c4df7349391e2..622cf04c71a9e 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -72,7 +72,7 @@ fn mir_borrowck<'a, 'tcx>( let input_mir = tcx.mir_validated(def_id); debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); - if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.use_mir() { + if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.use_mir() { return None; } @@ -101,7 +101,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( // contain non-lexical lifetimes. It will have a lifetime tied // to the inference context. let mut mir: Mir<'tcx> = input_mir.clone(); - let free_regions = if !tcx.sess.nll() { + let free_regions = if !tcx.nll() { None } else { let mir = &mut mir; @@ -204,7 +204,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( ); (Some(Rc::new(regioncx)), opt_closure_req) } else { - assert!(!tcx.sess.nll()); + assert!(!tcx.nll()); (None, None) }; let flow_inits = flow_inits; // remove mut @@ -712,7 +712,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// allowed to be split into separate Reservation and /// Activation phases. fn allow_two_phase_borrow(&self, kind: BorrowKind) -> bool { - self.tcx.sess.two_phase_borrows() && + self.tcx.two_phase_borrows() && (kind.allows_two_phase_borrow() || self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref) } @@ -1187,7 +1187,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { span: Span, flow_state: &Flows<'cx, 'gcx, 'tcx>, ) { - if !self.tcx.sess.two_phase_borrows() { + if !self.tcx.two_phase_borrows() { return; } diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index 932aad0bb1d84..1ed8289d44184 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -50,7 +50,7 @@ impl<'tcx> CFG<'tcx> { block: BasicBlock, source_info: SourceInfo, region_scope: region::Scope) { - if tcx.sess.emit_end_regions() { + if tcx.emit_end_regions() { if let region::ScopeData::CallSite(_) = region_scope.data() { // The CallSite scope (aka the root scope) is sort of weird, in that it is // supposed to "separate" the "interior" and "exterior" of a closure. Being diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index b16d7ed236509..abea55835466f 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -113,7 +113,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| { i == variant_index || { - self.hir.tcx().sess.features.borrow().never_type && + self.hir.tcx().features().never_type && self.hir.tcx().is_variant_uninhabited_from_all_modules(v, substs) } }); diff --git a/src/librustc_mir/transform/clean_end_regions.rs b/src/librustc_mir/transform/clean_end_regions.rs index 7986313aa8134..6e8985d99d287 100644 --- a/src/librustc_mir/transform/clean_end_regions.rs +++ b/src/librustc_mir/transform/clean_end_regions.rs @@ -42,7 +42,7 @@ impl MirPass for CleanEndRegions { tcx: TyCtxt<'a, 'tcx, 'tcx>, _source: MirSource, mir: &mut Mir<'tcx>) { - if !tcx.sess.emit_end_regions() { return; } + if !tcx.emit_end_regions() { return; } let mut gather = GatherBorrowedRegions { seen_regions: FxHashSet() diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 2b9ee223b01a4..bb2dc9a7494f9 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -904,7 +904,7 @@ This does not pose a problem by itself because they can't be accessed directly." if self.mode != Mode::Fn && // feature-gate is not enabled, - !self.tcx.sess.features.borrow() + !self.tcx.features() .declared_lib_features .iter() .any(|&(ref sym, _)| sym == feature_name) && diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 4a7ee397aec00..89242ca32bcbf 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -514,7 +514,7 @@ impl<'b, 'gcx, 'tcx> BorrowckErrors for TyCtxt<'b, 'gcx, 'tcx> { o: Origin) -> DiagnosticBuilder<'a> { - if !o.should_emit_errors(self.sess.borrowck_mode()) { + if !o.should_emit_errors(self.borrowck_mode()) { self.sess.diagnostic().cancel(&mut diag); } diag diff --git a/src/librustc_plugin/load.rs b/src/librustc_plugin/load.rs index a46b85d93cbb8..bf59165a9c461 100644 --- a/src/librustc_plugin/load.rs +++ b/src/librustc_plugin/load.rs @@ -52,7 +52,7 @@ pub fn load_plugins(sess: &Session, // do not report any error now. since crate attributes are // not touched by expansion, every use of plugin without // the feature enabled will result in an error later... - if sess.features.borrow().plugin { + if sess.features_untracked().plugin { for attr in &krate.attrs { if !attr.check_name("plugin") { continue; diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index c55bf395d71b3..7377adba6c14b 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -590,7 +590,7 @@ impl<'a> Resolver<'a> { }; let ext = Rc::new(macro_rules::compile(&self.session.parse_sess, - &self.session.features, + &self.session.features_untracked(), ¯o_def)); self.macro_map.insert(def_id, ext.clone()); ext diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 2da4bfedd3a17..ec0239272a677 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1523,7 +1523,7 @@ impl<'a> Resolver<'a> { invocations.insert(Mark::root(), arenas.alloc_invocation_data(InvocationData::root(graph_root))); - let features = session.features.borrow(); + let features = session.features_untracked(); let mut macro_defs = FxHashMap(); macro_defs.insert(Mark::root(), root_def_id); @@ -2994,7 +2994,7 @@ impl<'a> Resolver<'a> { let prim = self.primitive_type_table.primitive_types[&path[0].node.name]; match prim { TyUint(UintTy::U128) | TyInt(IntTy::I128) => { - if !self.session.features.borrow().i128_type { + if !self.session.features_untracked().i128_type { emit_feature_err(&self.session.parse_sess, "i128_type", span, GateIssue::Language, "128-bit type is unstable"); @@ -3085,7 +3085,7 @@ impl<'a> Resolver<'a> { let prev_name = path[0].node.name; if prev_name == keywords::Extern.name() || prev_name == keywords::CrateRoot.name() && - self.session.features.borrow().extern_absolute_paths { + self.session.features_untracked().extern_absolute_paths { // `::extern_crate::a::b` let crate_id = self.crate_loader.resolve_crate_from_path(name, ident.span); let crate_root = diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 080ef3252a633..69078d40045b6 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -744,7 +744,7 @@ impl<'a> Resolver<'a> { let def_id = self.definitions.local_def_id(item.id); let ext = Rc::new(macro_rules::compile(&self.session.parse_sess, - &self.session.features, + &self.session.features_untracked(), item)); self.macro_map.insert(def_id, ext); @@ -838,7 +838,7 @@ impl<'a> Resolver<'a> { } fn gate_legacy_custom_derive(&mut self, name: Symbol, span: Span) { - if !self.session.features.borrow().custom_derive { + if !self.session.features_untracked().custom_derive { let sess = &self.session.parse_sess; let explain = feature_gate::EXPLAIN_CUSTOM_DERIVE; emit_feature_err(sess, "custom_derive", span, GateIssue::Language, explain); diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 8cb25f449b667..d60638c0ee7ec 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -609,7 +609,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { if module_path.len() == 1 && (module_path[0].node.name == keywords::CrateRoot.name() || module_path[0].node.name == keywords::Extern.name()) { let is_extern = module_path[0].node.name == keywords::Extern.name() || - self.session.features.borrow().extern_absolute_paths; + self.session.features_untracked().extern_absolute_paths; match directive.subclass { GlobImport { .. } if is_extern => { return Some((directive.span, diff --git a/src/librustc_trans_utils/symbol_names_test.rs b/src/librustc_trans_utils/symbol_names_test.rs index 5d7d4f3055bad..267c8d2bd03c8 100644 --- a/src/librustc_trans_utils/symbol_names_test.rs +++ b/src/librustc_trans_utils/symbol_names_test.rs @@ -28,7 +28,7 @@ pub fn report_symbol_names<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { // if the `rustc_attrs` feature is not enabled, then the // attributes we are interested in cannot be present anyway, so // skip the walk. - if !tcx.sess.features.borrow().rustc_attrs { + if !tcx.features().rustc_attrs { return; } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 1139ea5fbd364..b94917ae04dc8 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -417,7 +417,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let trait_def = self.tcx().trait_def(trait_def_id); - if !self.tcx().sess.features.borrow().unboxed_closures && + if !self.tcx().features().unboxed_closures && trait_segment.with_parameters(|p| p.parenthesized) != trait_def.paren_sugar { // For now, require that parenthetical notation be used only with `Fn()` etc. let msg = if trait_def.paren_sugar { diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index bf253a88d27c2..bb6ed89433562 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -114,7 +114,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; if pat_adjustments.len() > 0 { - if tcx.sess.features.borrow().match_default_bindings { + if tcx.features().match_default_bindings { debug!("default binding mode is now {:?}", def_bm); self.inh.tables.borrow_mut() .pat_adjustments_mut() diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 47e4b0272bed4..3a153bafe8f50 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -585,7 +585,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { } } - if has_unsized_tuple_coercion && !self.tcx.sess.features.borrow().unsized_tuple_coercion { + if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion { feature_gate::emit_feature_err(&self.tcx.sess.parse_sess, "unsized_tuple_coercion", self.cause.span, diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index e8c3966f23f08..f3850da0ba557 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -324,7 +324,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // possible that there will be multiple applicable methods. if !is_suggestion.0 { if reached_raw_pointer - && !self.tcx.sess.features.borrow().arbitrary_self_types { + && !self.tcx.features().arbitrary_self_types { // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 epoch // (see https://github.com/rust-lang/rust/issues/46906) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 165b499cc62aa..73e24ff5252df 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1089,7 +1089,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, } fcx.demand_suptype(span, ret_ty, actual_return_ty); - if fcx.tcx.sess.features.borrow().termination_trait { + if fcx.tcx.features().termination_trait { // If the termination trait language item is activated, check that the main return type // implements the termination trait. if let Some(term_id) = fcx.tcx.lang_items().termination() { @@ -1600,7 +1600,7 @@ pub fn check_enum<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let repr_type_ty = def.repr.discr_type().to_ty(tcx); if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 { - if !tcx.sess.features.borrow().repr128 { + if !tcx.features().repr128 { emit_feature_err(&tcx.sess.parse_sess, "repr128", sp, diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 3668fc46ddc27..e2d640fd67e6b 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -471,7 +471,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { let is_self_ty = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok(); let self_kind = ExplicitSelf::determine(self_arg_ty, is_self_ty); - if !fcx.tcx.sess.features.borrow().arbitrary_self_types { + if !fcx.tcx.features().arbitrary_self_types { match self_kind { ExplicitSelf::ByValue | ExplicitSelf::ByReference(_, _) | diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index f65d627781f0f..d3de31d630a97 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -74,7 +74,7 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt, impl_def_id: DefId, trait_d return; } - if tcx.sess.features.borrow().unboxed_closures { + if tcx.features().unboxed_closures { // the feature gate allows all Fn traits return; } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index d5328a18c2240..8234dc95c7996 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -711,7 +711,7 @@ fn trait_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }; let paren_sugar = tcx.has_attr(def_id, "rustc_paren_sugar"); - if paren_sugar && !tcx.sess.features.borrow().unboxed_closures { + if paren_sugar && !tcx.features().unboxed_closures { let mut err = tcx.sess.struct_span_err( item.span, "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \ @@ -953,7 +953,7 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } if !allow_defaults && p.default.is_some() { - if !tcx.sess.features.borrow().default_type_parameter_fallback { + if !tcx.features().default_type_parameter_fallback { tcx.lint_node( lint::builtin::INVALID_TYPE_PARAM_DEFAULT, p.id, @@ -1674,7 +1674,7 @@ fn compute_sig_of_foreign_fn_decl<'a, 'tcx>( // feature gate SIMD types in FFI, since I (huonw) am not sure the // ABIs are handled at all correctly. if abi != abi::Abi::RustIntrinsic && abi != abi::Abi::PlatformIntrinsic - && !tcx.sess.features.borrow().simd_ffi { + && !tcx.features().simd_ffi { let check = |ast_ty: &hir::Ty, ty: Ty| { if ty.is_simd() { tcx.sess.struct_span_err(ast_ty.span, diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index bd7e200d620e6..01ceb8eb29151 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -207,7 +207,7 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let actual = tcx.fn_sig(main_def_id); let expected_return_type = if tcx.lang_items().termination().is_some() - && tcx.sess.features.borrow().termination_trait { + && tcx.features().termination_trait { // we take the return type of the given main function, the real check is done // in `check_fn` actual.output().skip_binder() diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 087d88419bc84..b1dd3db9d861d 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -665,7 +665,7 @@ impl<'a, 'hir> HirCollector<'a, 'hir> { nested: F) { let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs); if let Some(ref cfg) = attrs.cfg { - if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features.borrow())) { + if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) { return; } } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 5254c751e6b62..f8c95ae17bf31 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -26,7 +26,6 @@ use parse::token::Token::*; use symbol::Symbol; use tokenstream::{TokenStream, TokenTree}; -use std::cell::RefCell; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::rc::Rc; @@ -183,7 +182,7 @@ 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: &RefCell, def: &ast::Item) -> SyntaxExtension { +pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> SyntaxExtension { let lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs")); let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs")); @@ -295,7 +294,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) } fn check_lhs_nt_follows(sess: &ParseSess, - features: &RefCell, + features: &Features, attrs: &[ast::Attribute], lhs: "ed::TokenTree) -> bool { // lhs is going to be like TokenTree::Delimited(...), where the @@ -352,7 +351,7 @@ fn check_rhs(sess: &ParseSess, rhs: "ed::TokenTree) -> bool { } fn check_matcher(sess: &ParseSess, - features: &RefCell, + features: &Features, attrs: &[ast::Attribute], matcher: &[quoted::TokenTree]) -> bool { let first_sets = FirstSets::new(matcher); @@ -600,7 +599,7 @@ impl TokenSet { // Requires that `first_sets` is pre-computed for `matcher`; // see `FirstSets::new`. fn check_matcher_core(sess: &ParseSess, - features: &RefCell, + features: &Features, attrs: &[ast::Attribute], first_sets: &FirstSets, matcher: &[quoted::TokenTree], @@ -868,7 +867,7 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result, + features: &Features, attrs: &[ast::Attribute], tok: "ed::TokenTree) -> Result<(), String> { debug!("has_legal_fragment_specifier({:?})", tok); @@ -883,7 +882,7 @@ fn has_legal_fragment_specifier(sess: &ParseSess, } fn is_legal_fragment_specifier(sess: &ParseSess, - features: &RefCell, + features: &Features, attrs: &[ast::Attribute], frag_name: &str, frag_span: Span) -> bool { @@ -891,7 +890,7 @@ fn is_legal_fragment_specifier(sess: &ParseSess, "item" | "block" | "stmt" | "expr" | "pat" | "path" | "ty" | "ident" | "meta" | "tt" | "" => true, "lifetime" => { - if !features.borrow().macro_lifetime_matcher && + if !features.macro_lifetime_matcher && !attr::contains_name(attrs, "allow_internal_unstable") { let explain = feature_gate::EXPLAIN_LIFETIME_MATCHER; emit_feature_err(sess, @@ -903,7 +902,7 @@ fn is_legal_fragment_specifier(sess: &ParseSess, true }, "vis" => { - if !features.borrow().macro_vis_matcher && + if !features.macro_vis_matcher && !attr::contains_name(attrs, "allow_internal_unstable") { let explain = feature_gate::EXPLAIN_VIS_MATCHER; emit_feature_err(sess, diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 982b60b81e47e..c6484e9a7706a 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -17,7 +17,6 @@ use symbol::keywords; use syntax_pos::{BytePos, Span, DUMMY_SP}; use tokenstream; -use std::cell::RefCell; use std::iter::Peekable; use std::rc::Rc; @@ -183,7 +182,7 @@ pub fn parse( input: tokenstream::TokenStream, expect_matchers: bool, sess: &ParseSess, - features: &RefCell, + features: &Features, attrs: &[ast::Attribute], ) -> Vec { // Will contain the final collection of `self::TokenTree` @@ -251,7 +250,7 @@ fn parse_tree( trees: &mut Peekable, expect_matchers: bool, sess: &ParseSess, - features: &RefCell, + features: &Features, attrs: &[ast::Attribute], ) -> TokenTree where @@ -382,7 +381,7 @@ fn parse_sep_and_kleene_op( input: &mut Peekable, span: Span, sess: &ParseSess, - features: &RefCell, + features: &Features, attrs: &[ast::Attribute], ) -> (Option, KleeneOp) where @@ -415,7 +414,7 @@ where match parse_kleene_op(input, span) { // #2 is a KleeneOp (this is the only valid option) :) Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => { - if !features.borrow().macro_at_most_once_rep + if !features.macro_at_most_once_rep && !attr::contains_name(attrs, "allow_internal_unstable") { let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; @@ -438,7 +437,7 @@ where Err(span) => span, } } else { - if !features.borrow().macro_at_most_once_rep + if !features.macro_at_most_once_rep && !attr::contains_name(attrs, "allow_internal_unstable") { let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; @@ -460,7 +459,7 @@ where Ok(Err((tok, span))) => match parse_kleene_op(input, span) { // #2 is a KleeneOp :D Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => { - if !features.borrow().macro_at_most_once_rep + if !features.macro_at_most_once_rep && !attr::contains_name(attrs, "allow_internal_unstable") { let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; @@ -487,7 +486,7 @@ where Err(span) => span, }; - if !features.borrow().macro_at_most_once_rep + if !features.macro_at_most_once_rep && !attr::contains_name(attrs, "allow_internal_unstable") { sess.span_diagnostic diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ea916d5168c33..8369520d0d6e5 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -62,6 +62,7 @@ macro_rules! declare_features { &[$((stringify!($feature), $ver, $issue, set!($feature))),+]; /// A set of features to be used by later passes. + #[derive(Clone)] pub struct Features { /// `#![feature]` attrs for stable language features, for error reporting pub declared_stable_lang_features: Vec<(Symbol, Span)>, @@ -78,6 +79,12 @@ macro_rules! declare_features { $($feature: false),+ } } + + pub fn walk_feature_fields(&self, mut f: F) + where F: FnMut(&str, bool) + { + $(f(stringify!($feature), self.$feature);)+ + } } }; From c0517a38aced792365af440e9140d91518b9bb45 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 14 Feb 2018 16:26:56 +0100 Subject: [PATCH 19/42] incr.comp.: Add regression test for detecting feature gate changes. --- src/test/incremental/feature_gate.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/test/incremental/feature_gate.rs diff --git a/src/test/incremental/feature_gate.rs b/src/test/incremental/feature_gate.rs new file mode 100644 index 0000000000000..de2f9ab52f60b --- /dev/null +++ b/src/test/incremental/feature_gate.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test makes sure that we detect changed feature gates. + +// revisions:rpass1 cfail2 +// compile-flags: -Z query-dep-graph + +#![feature(rustc_attrs)] +#![cfg_attr(rpass1, feature(nll))] + +fn main() { + let mut v = vec![1]; + v.push(v[0]); + //[cfail2]~^ ERROR cannot borrow +} From d691c463f6248e20e0dbf57745fee07a5b1eb5a8 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 15 Feb 2018 15:53:26 +0100 Subject: [PATCH 20/42] Fix procedural_mbe_matching test case after libsyntax change. --- src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs b/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs index 9ebc438ad5a00..fd8f7b9e384f3 100644 --- a/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs +++ b/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs @@ -41,7 +41,7 @@ fn expand_mbe_matches(cx: &mut ExtCtxt, _: Span, args: &[TokenTree]) let mbe_matcher = quoted::parse(mbe_matcher.into_iter().collect(), true, cx.parse_sess, - &RefCell::new(Features::new()), + &Features::new(), &[]); let map = match TokenTree::parse(cx, &mbe_matcher, args.iter().cloned().collect()) { Success(map) => map, From 1f0e1a043959461afd061be7ff92362492b2c85d Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Thu, 15 Feb 2018 00:06:14 +0100 Subject: [PATCH 21/42] Convert compile-fail/lint-ctypes.rs to ui test --- src/test/{compile-fail => ui}/lint-ctypes.rs | 0 src/test/ui/lint-ctypes.stderr | 128 +++++++++++++++++++ 2 files changed, 128 insertions(+) rename src/test/{compile-fail => ui}/lint-ctypes.rs (100%) create mode 100644 src/test/ui/lint-ctypes.stderr diff --git a/src/test/compile-fail/lint-ctypes.rs b/src/test/ui/lint-ctypes.rs similarity index 100% rename from src/test/compile-fail/lint-ctypes.rs rename to src/test/ui/lint-ctypes.rs diff --git a/src/test/ui/lint-ctypes.stderr b/src/test/ui/lint-ctypes.stderr new file mode 100644 index 0000000000000..21aadde8ac87c --- /dev/null +++ b/src/test/ui/lint-ctypes.stderr @@ -0,0 +1,128 @@ +error: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type + --> $DIR/lint-ctypes.rs:54:28 + | +54 | pub fn ptr_type1(size: *const Foo); //~ ERROR: found struct without + | ^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/lint-ctypes.rs:11:9 + | +11 | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + +error: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type + --> $DIR/lint-ctypes.rs:55:28 + | +55 | pub fn ptr_type2(size: *const Foo); //~ ERROR: found struct without + | ^^^^^^^^^^ + +error: found Rust slice type in foreign module, consider using a raw pointer instead + --> $DIR/lint-ctypes.rs:56:26 + | +56 | pub fn slice_type(p: &[u32]); //~ ERROR: found Rust slice type + | ^^^^^^ + +error: found Rust type `str` in foreign module; consider using a `*const libc::c_char` + --> $DIR/lint-ctypes.rs:57:24 + | +57 | pub fn str_type(p: &str); //~ ERROR: found Rust type + | ^^^^ + +error: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type + --> $DIR/lint-ctypes.rs:58:24 + | +58 | pub fn box_type(p: Box); //~ ERROR found struct without + | ^^^^^^^^ + +error: found Rust type `char` in foreign module, while `u32` or `libc::wchar_t` should be used + --> $DIR/lint-ctypes.rs:59:25 + | +59 | pub fn char_type(p: char); //~ ERROR found Rust type + | ^^^^ + +error: found Rust type `i128` in foreign module, but 128-bit integers don't currently have a known stable ABI + --> $DIR/lint-ctypes.rs:60:25 + | +60 | pub fn i128_type(p: i128); //~ ERROR found Rust type + | ^^^^ + +error: found Rust type `u128` in foreign module, but 128-bit integers don't currently have a known stable ABI + --> $DIR/lint-ctypes.rs:61:25 + | +61 | pub fn u128_type(p: u128); //~ ERROR found Rust type + | ^^^^ + +error: found Rust trait type in foreign module, consider using a raw pointer instead + --> $DIR/lint-ctypes.rs:62:26 + | +62 | pub fn trait_type(p: &Clone); //~ ERROR found Rust trait type + | ^^^^^^ + +error: found Rust tuple type in foreign module; consider using a struct instead + --> $DIR/lint-ctypes.rs:63:26 + | +63 | pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type + | ^^^^^^^^^^ + +error: found Rust tuple type in foreign module; consider using a struct instead + --> $DIR/lint-ctypes.rs:64:27 + | +64 | pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type + | ^^^^^^^ + +error: found zero-size struct in foreign module, consider adding a member to this struct + --> $DIR/lint-ctypes.rs:65:25 + | +65 | pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct + | ^^^^^^^^ + +error: found zero-sized type composed only of phantom-data in a foreign-function. + --> $DIR/lint-ctypes.rs:66:33 + | +66 | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR found zero-sized type + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: found zero-sized type composed only of phantom-data in a foreign-function. + --> $DIR/lint-ctypes.rs:68:12 + | +68 | -> ::std::marker::PhantomData; //~ ERROR: found zero-sized type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: found function pointer with Rust calling convention in foreign module; consider using an `extern` function pointer + --> $DIR/lint-ctypes.rs:69:23 + | +69 | pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust + | ^^^^^^ + +error: found function pointer with Rust calling convention in foreign module; consider using an `extern` function pointer + --> $DIR/lint-ctypes.rs:70:24 + | +70 | pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust + | ^^^^ + +error: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type + --> $DIR/lint-ctypes.rs:71:28 + | +71 | pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without + | ^^^^^^^^^^ + +error: found non-foreign-function-safe member in struct marked #[repr(C)]: found Rust type `i128` in foreign module, but 128-bit integers don't currently have a known stable ABI + --> $DIR/lint-ctypes.rs:72:32 + | +72 | pub fn transparent_i128(p: TransparentI128); //~ ERROR: found Rust type `i128` + | ^^^^^^^^^^^^^^^ + +error: found non-foreign-function-safe member in struct marked #[repr(C)]: found Rust type `str` in foreign module; consider using a `*const libc::c_char` + --> $DIR/lint-ctypes.rs:73:31 + | +73 | pub fn transparent_str(p: TransparentStr); //~ ERROR: found Rust type `str` + | ^^^^^^^^^^^^^^ + +error: found non-foreign-function-safe member in struct marked #[repr(C)]: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type + --> $DIR/lint-ctypes.rs:74:30 + | +74 | pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: found struct without + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 20 previous errors + From 7ac5e96f4af8efe4a7f09a873d81006329cb5133 Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Sun, 11 Feb 2018 21:31:42 +0100 Subject: [PATCH 22/42] [improper_ctypes] Use a 'help:' line for possible fixes --- src/librustc_lint/types.rs | 177 +++++++++++++++++++++------------ src/test/ui/lint-ctypes.stderr | 59 ++++++++--- 2 files changed, 156 insertions(+), 80 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index f734f3182a931..b6e8ae9694269 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -353,13 +353,18 @@ struct ImproperCTypesVisitor<'a, 'tcx: 'a> { cx: &'a LateContext<'a, 'tcx>, } +struct FfiError { + message: &'static str, + help: Option<&'static str>, +} + enum FfiResult { FfiSafe, FfiPhantom, - FfiUnsafe(&'static str), - FfiBadStruct(DefId, &'static str), - FfiBadUnion(DefId, &'static str), - FfiBadEnum(DefId, &'static str), + FfiUnsafe(FfiError), + FfiBadStruct(DefId, FfiError), + FfiBadUnion(DefId, FfiError), + FfiBadEnum(DefId, FfiError), } /// Check if this enum can be safely exported based on the @@ -434,14 +439,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match def.adt_kind() { AdtKind::Struct => { if !def.repr.c() && !def.repr.transparent() { - return FfiUnsafe("found struct without foreign-function-safe \ - representation annotation in foreign module, \ - consider adding a #[repr(C)] attribute to the type"); + return FfiUnsafe(FfiError { + message: "found struct without foreign-function-safe \ + representation annotation in foreign module", + help: Some("consider adding a #[repr(C)] attribute to the type"), + }); } if def.non_enum_variant().fields.is_empty() { - return FfiUnsafe("found zero-size struct in foreign module, consider \ - adding a member to this struct"); + return FfiUnsafe(FfiError { + message: "found zero-size struct in foreign module", + help: Some("consider adding a member to this struct"), + }); } // We can't completely trust repr(C) and repr(transparent) markings; @@ -471,8 +480,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; } - FfiUnsafe(s) => { - return FfiBadStruct(def.did, s); + FfiUnsafe(err) => { + return FfiBadStruct(def.did, err); } } } @@ -481,14 +490,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } AdtKind::Union => { if !def.repr.c() { - return FfiUnsafe("found union without foreign-function-safe \ - representation annotation in foreign module, \ - consider adding a #[repr(C)] attribute to the type"); + return FfiUnsafe(FfiError { + message: "found union without foreign-function-safe \ + representation annotation in foreign module", + help: Some("consider adding a #[repr(C)] attribute to the type"), + }); } if def.non_enum_variant().fields.is_empty() { - return FfiUnsafe("found zero-size union in foreign module, consider \ - adding a member to this union"); + return FfiUnsafe(FfiError { + message: "found zero-size union in foreign module", + help: Some("consider adding a member to this union"), + }); } let mut all_phantom = true; @@ -505,8 +518,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; } - FfiUnsafe(s) => { - return FfiBadUnion(def.did, s); + FfiUnsafe(err) => { + return FfiBadUnion(def.did, err); } } } @@ -524,10 +537,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if !def.repr.c() && def.repr.int.is_none() { // Special-case types like `Option`. if !is_repr_nullable_ptr(cx, def, substs) { - return FfiUnsafe("found enum without foreign-function-safe \ - representation annotation in foreign \ - module, consider adding a #[repr(...)] \ - attribute to the type"); + return FfiUnsafe(FfiError { + message: "found enum without foreign-function-safe \ + representation annotation in foreign module", + help: Some("consider adding a #[repr(...)] attribute \ + to the type"), + }); } } @@ -535,7 +550,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if !is_ffi_safe(int_ty) { // FIXME: This shouldn't be reachable: we should check // this earlier. - return FfiUnsafe("enum has unexpected #[repr(...)] attribute"); + return FfiUnsafe(FfiError { + message: "enum has unexpected #[repr(...)] attribute", + help: None, + }); } // Enum with an explicitly sized discriminant; either @@ -558,11 +576,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return r; } FfiPhantom => { - return FfiBadEnum(def.did, - "Found phantom data in enum variant"); + return FfiBadEnum(def.did, FfiError { + message: "Found phantom data in enum variant", + help: None, + }); } - FfiUnsafe(s) => { - return FfiBadEnum(def.did, s); + FfiUnsafe(err) => { + return FfiBadEnum(def.did, err); } } } @@ -573,43 +593,57 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } ty::TyChar => { - FfiUnsafe("found Rust type `char` in foreign module, while \ - `u32` or `libc::wchar_t` should be used") + FfiUnsafe(FfiError { + message: "found Rust type `char` in foreign module", + help: Some("consider using `u32` or `libc::wchar_t`"), + }) } ty::TyInt(ast::IntTy::I128) => { - FfiUnsafe("found Rust type `i128` in foreign module, but \ - 128-bit integers don't currently have a known \ - stable ABI") + FfiUnsafe(FfiError { + message: "found Rust type `i128` in foreign module, but 128-bit \ + integers don't currently have a known stable ABI", + help: None, + }) } ty::TyUint(ast::UintTy::U128) => { - FfiUnsafe("found Rust type `u128` in foreign module, but \ - 128-bit integers don't currently have a known \ - stable ABI") + FfiUnsafe(FfiError { + message: "found Rust type `u128` in foreign module, but 128-bit \ + integers don't currently have a known stable ABI", + help: None, + }) } // Primitive types with a stable representation. ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe, ty::TySlice(_) => { - FfiUnsafe("found Rust slice type in foreign module, \ - consider using a raw pointer instead") + FfiUnsafe(FfiError { + message: "found Rust slice type in foreign module", + help: Some("consider using a raw pointer instead"), + }) } ty::TyDynamic(..) => { - FfiUnsafe("found Rust trait type in foreign module, \ - consider using a raw pointer instead") + FfiUnsafe(FfiError { + message: "found Rust trait type in foreign module", + help: Some("consider using a raw pointer instead"), + }) } ty::TyStr => { - FfiUnsafe("found Rust type `str` in foreign module; \ - consider using a `*const libc::c_char`") + FfiUnsafe(FfiError { + message: "found Rust type `str` in foreign module", + help: Some("consider using a `*const libc::c_char`"), + }) } ty::TyTuple(..) => { - FfiUnsafe("found Rust tuple type in foreign module; \ - consider using a struct instead") + FfiUnsafe(FfiError { + message: "found Rust tuple type in foreign module", + help: Some("consider using a struct instead"), + }) } ty::TyRawPtr(ref m) | @@ -620,9 +654,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::TyFnPtr(sig) => { match sig.abi() { Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => { - return FfiUnsafe("found function pointer with Rust calling convention in \ - foreign module; consider using an `extern` function \ - pointer") + return FfiUnsafe(FfiError { + message: "found function pointer with Rust calling convention in \ + foreign module", + help: Some("consider using an `extern` function pointer"), + }) } _ => {} } @@ -676,34 +712,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { &format!("found zero-sized type composed only \ of phantom-data in a foreign-function.")); } - FfiResult::FfiUnsafe(s) => { - self.cx.span_lint(IMPROPER_CTYPES, sp, s); + FfiResult::FfiUnsafe(err) => { + let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, err.message); + if let Some(s) = err.help { + diag.help(s); + } + diag.emit(); } - FfiResult::FfiBadStruct(_, s) => { + FfiResult::FfiBadStruct(_, err) => { // FIXME: This diagnostic is difficult to read, and doesn't // point at the relevant field. - self.cx.span_lint(IMPROPER_CTYPES, - sp, - &format!("found non-foreign-function-safe member in struct \ - marked #[repr(C)]: {}", - s)); + let msg = format!("found non-foreign-function-safe member in struct \ + marked #[repr(C)]: {}", err.message); + let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg); + if let Some(s) = err.help { + diag.help(s); + } + diag.emit(); } - FfiResult::FfiBadUnion(_, s) => { + FfiResult::FfiBadUnion(_, err) => { // FIXME: This diagnostic is difficult to read, and doesn't // point at the relevant field. - self.cx.span_lint(IMPROPER_CTYPES, - sp, - &format!("found non-foreign-function-safe member in union \ - marked #[repr(C)]: {}", - s)); + let msg = format!("found non-foreign-function-safe member in union \ + marked #[repr(C)]: {}", err.message); + let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg); + if let Some(s) = err.help { + diag.help(s); + } + diag.emit(); } - FfiResult::FfiBadEnum(_, s) => { + FfiResult::FfiBadEnum(_, err) => { // FIXME: This diagnostic is difficult to read, and doesn't // point at the relevant variant. - self.cx.span_lint(IMPROPER_CTYPES, - sp, - &format!("found non-foreign-function-safe member in enum: {}", - s)); + let msg = format!("found non-foreign-function-safe member in enum: {}", + err.message); + let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg); + if let Some(s) = err.help { + diag.help(s); + } + diag.emit(); } } } diff --git a/src/test/ui/lint-ctypes.stderr b/src/test/ui/lint-ctypes.stderr index 21aadde8ac87c..0f7f7e048e3ab 100644 --- a/src/test/ui/lint-ctypes.stderr +++ b/src/test/ui/lint-ctypes.stderr @@ -1,4 +1,4 @@ -error: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type +error: found struct without foreign-function-safe representation annotation in foreign module --> $DIR/lint-ctypes.rs:54:28 | 54 | pub fn ptr_type1(size: *const Foo); //~ ERROR: found struct without @@ -9,36 +9,47 @@ note: lint level defined here | 11 | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ + = help: consider adding a #[repr(C)] attribute to the type -error: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type +error: found struct without foreign-function-safe representation annotation in foreign module --> $DIR/lint-ctypes.rs:55:28 | 55 | pub fn ptr_type2(size: *const Foo); //~ ERROR: found struct without | ^^^^^^^^^^ + | + = help: consider adding a #[repr(C)] attribute to the type -error: found Rust slice type in foreign module, consider using a raw pointer instead +error: found Rust slice type in foreign module --> $DIR/lint-ctypes.rs:56:26 | 56 | pub fn slice_type(p: &[u32]); //~ ERROR: found Rust slice type | ^^^^^^ + | + = help: consider using a raw pointer instead -error: found Rust type `str` in foreign module; consider using a `*const libc::c_char` +error: found Rust type `str` in foreign module --> $DIR/lint-ctypes.rs:57:24 | 57 | pub fn str_type(p: &str); //~ ERROR: found Rust type | ^^^^ + | + = help: consider using a `*const libc::c_char` -error: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type +error: found struct without foreign-function-safe representation annotation in foreign module --> $DIR/lint-ctypes.rs:58:24 | 58 | pub fn box_type(p: Box); //~ ERROR found struct without | ^^^^^^^^ + | + = help: consider adding a #[repr(C)] attribute to the type -error: found Rust type `char` in foreign module, while `u32` or `libc::wchar_t` should be used +error: found Rust type `char` in foreign module --> $DIR/lint-ctypes.rs:59:25 | 59 | pub fn char_type(p: char); //~ ERROR found Rust type | ^^^^ + | + = help: consider using `u32` or `libc::wchar_t` error: found Rust type `i128` in foreign module, but 128-bit integers don't currently have a known stable ABI --> $DIR/lint-ctypes.rs:60:25 @@ -52,29 +63,37 @@ error: found Rust type `u128` in foreign module, but 128-bit integers don't curr 61 | pub fn u128_type(p: u128); //~ ERROR found Rust type | ^^^^ -error: found Rust trait type in foreign module, consider using a raw pointer instead +error: found Rust trait type in foreign module --> $DIR/lint-ctypes.rs:62:26 | 62 | pub fn trait_type(p: &Clone); //~ ERROR found Rust trait type | ^^^^^^ + | + = help: consider using a raw pointer instead -error: found Rust tuple type in foreign module; consider using a struct instead +error: found Rust tuple type in foreign module --> $DIR/lint-ctypes.rs:63:26 | 63 | pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type | ^^^^^^^^^^ + | + = help: consider using a struct instead -error: found Rust tuple type in foreign module; consider using a struct instead +error: found Rust tuple type in foreign module --> $DIR/lint-ctypes.rs:64:27 | 64 | pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type | ^^^^^^^ + | + = help: consider using a struct instead -error: found zero-size struct in foreign module, consider adding a member to this struct +error: found zero-size struct in foreign module --> $DIR/lint-ctypes.rs:65:25 | 65 | pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct | ^^^^^^^^ + | + = help: consider adding a member to this struct error: found zero-sized type composed only of phantom-data in a foreign-function. --> $DIR/lint-ctypes.rs:66:33 @@ -88,23 +107,29 @@ error: found zero-sized type composed only of phantom-data in a foreign-function 68 | -> ::std::marker::PhantomData; //~ ERROR: found zero-sized type | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: found function pointer with Rust calling convention in foreign module; consider using an `extern` function pointer +error: found function pointer with Rust calling convention in foreign module --> $DIR/lint-ctypes.rs:69:23 | 69 | pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust | ^^^^^^ + | + = help: consider using an `extern` function pointer -error: found function pointer with Rust calling convention in foreign module; consider using an `extern` function pointer +error: found function pointer with Rust calling convention in foreign module --> $DIR/lint-ctypes.rs:70:24 | 70 | pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust | ^^^^ + | + = help: consider using an `extern` function pointer -error: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type +error: found struct without foreign-function-safe representation annotation in foreign module --> $DIR/lint-ctypes.rs:71:28 | 71 | pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without | ^^^^^^^^^^ + | + = help: consider adding a #[repr(C)] attribute to the type error: found non-foreign-function-safe member in struct marked #[repr(C)]: found Rust type `i128` in foreign module, but 128-bit integers don't currently have a known stable ABI --> $DIR/lint-ctypes.rs:72:32 @@ -112,17 +137,21 @@ error: found non-foreign-function-safe member in struct marked #[repr(C)]: found 72 | pub fn transparent_i128(p: TransparentI128); //~ ERROR: found Rust type `i128` | ^^^^^^^^^^^^^^^ -error: found non-foreign-function-safe member in struct marked #[repr(C)]: found Rust type `str` in foreign module; consider using a `*const libc::c_char` +error: found non-foreign-function-safe member in struct marked #[repr(C)]: found Rust type `str` in foreign module --> $DIR/lint-ctypes.rs:73:31 | 73 | pub fn transparent_str(p: TransparentStr); //~ ERROR: found Rust type `str` | ^^^^^^^^^^^^^^ + | + = help: consider using a `*const libc::c_char` -error: found non-foreign-function-safe member in struct marked #[repr(C)]: found struct without foreign-function-safe representation annotation in foreign module, consider adding a #[repr(C)] attribute to the type +error: found non-foreign-function-safe member in struct marked #[repr(C)]: found struct without foreign-function-safe representation annotation in foreign module --> $DIR/lint-ctypes.rs:74:30 | 74 | pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: found struct without | ^^^^^^^^^^^^^^^^ + | + = help: consider adding a #[repr(C)] attribute to the type error: aborting due to 20 previous errors From ae92dfac5019643b8fb310de9e92f0889b0106ca Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Sun, 11 Feb 2018 21:54:56 +0100 Subject: [PATCH 23/42] [improper_ctypes] Stop complaining about repr(usize) and repr(isize) enums This dates back to at least #26583. At the time, usize and isize were considered ffi-unsafe to nudge people away from them, but this changed in the aforementioned PR, making it inconsistent to complain about it in enum discriminants. In fact, repr(usize) is probably the best way to interface with `enum Foo : size_t { ... }`. --- src/librustc_lint/types.rs | 29 ----------------------- src/test/compile-fail/lint-ctypes-enum.rs | 12 ++++++++++ 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index b6e8ae9694269..f17fa9c7ca1d2 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -26,7 +26,6 @@ use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; use syntax::ast; use syntax::abi::Abi; -use syntax::attr; use syntax_pos::Span; use syntax::codemap; @@ -402,17 +401,6 @@ fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, false } -fn is_ffi_safe(ty: attr::IntType) -> bool { - match ty { - attr::SignedInt(ast::IntTy::I8) | attr::UnsignedInt(ast::UintTy::U8) | - attr::SignedInt(ast::IntTy::I16) | attr::UnsignedInt(ast::UintTy::U16) | - attr::SignedInt(ast::IntTy::I32) | attr::UnsignedInt(ast::UintTy::U32) | - attr::SignedInt(ast::IntTy::I64) | attr::UnsignedInt(ast::UintTy::U64) | - attr::SignedInt(ast::IntTy::I128) | attr::UnsignedInt(ast::UintTy::U128) => true, - attr::SignedInt(ast::IntTy::Isize) | attr::UnsignedInt(ast::UintTy::Usize) => false - } -} - impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Check if the given type is "ffi-safe" (has a stable, well-defined /// representation which can be exported to C code). @@ -546,23 +534,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - if let Some(int_ty) = def.repr.int { - if !is_ffi_safe(int_ty) { - // FIXME: This shouldn't be reachable: we should check - // this earlier. - return FfiUnsafe(FfiError { - message: "enum has unexpected #[repr(...)] attribute", - help: None, - }); - } - - // Enum with an explicitly sized discriminant; either - // a C-style enum or a discriminated union. - - // The layout of enum variants is implicitly repr(C). - // FIXME: Is that correct? - } - // Check the contained variants. for variant in &def.variants { for field in &variant.fields { diff --git a/src/test/compile-fail/lint-ctypes-enum.rs b/src/test/compile-fail/lint-ctypes-enum.rs index e35dadbea9d4d..ce6afe1d219ea 100644 --- a/src/test/compile-fail/lint-ctypes-enum.rs +++ b/src/test/compile-fail/lint-ctypes-enum.rs @@ -16,11 +16,23 @@ enum U { A } enum B { C, D } enum T { E, F, G } +#[repr(C)] +enum ReprC { A, B, C } + +#[repr(u8)] +enum U8 { A, B, C } + +#[repr(isize)] +enum Isize { A, B, C } + extern { fn zf(x: Z); fn uf(x: U); //~ ERROR found enum without foreign-function-safe fn bf(x: B); //~ ERROR found enum without foreign-function-safe fn tf(x: T); //~ ERROR found enum without foreign-function-safe + fn reprc(x: ReprC); + fn u8(x: U8); + fn isize(x: Isize); } pub fn main() { } From 9b5f47ec48e8c12b68cff7cc64afe166358183ef Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Mon, 12 Feb 2018 01:08:48 +0100 Subject: [PATCH 24/42] [improper_ctypes] Overhaul primary label - Always name the non-FFI-safe - Explain *why* the type is not FFI-safe - Stop vaguely gesturing at structs/enums/unions if the non-FFI-safe types occured in a field. The last part is arguably a regression, but it's minor now that the non-FFI-safe type is actually named. Removing it avoids some code duplication. --- src/librustc_lint/types.rs | 238 ++++++++------------ src/test/compile-fail/issue-14309.rs | 10 +- src/test/compile-fail/issue-16250.rs | 2 +- src/test/compile-fail/lint-ctypes-enum.rs | 6 +- src/test/compile-fail/union/union-repr-c.rs | 2 +- src/test/ui/lint-ctypes.rs | 40 ++-- src/test/ui/lint-ctypes.stderr | 100 ++++---- 7 files changed, 171 insertions(+), 227 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index f17fa9c7ca1d2..7203b1b1e7dae 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -10,7 +10,6 @@ #![allow(non_snake_case)] -use rustc::hir::def_id::DefId; use rustc::hir::map as hir_map; use rustc::ty::subst::Substs; use rustc::ty::{self, AdtKind, Ty, TyCtxt}; @@ -352,18 +351,14 @@ struct ImproperCTypesVisitor<'a, 'tcx: 'a> { cx: &'a LateContext<'a, 'tcx>, } -struct FfiError { - message: &'static str, - help: Option<&'static str>, -} - -enum FfiResult { +enum FfiResult<'tcx> { FfiSafe, - FfiPhantom, - FfiUnsafe(FfiError), - FfiBadStruct(DefId, FfiError), - FfiBadUnion(DefId, FfiError), - FfiBadEnum(DefId, FfiError), + FfiPhantom(Ty<'tcx>), + FfiUnsafe { + ty: Ty<'tcx>, + reason: &'static str, + help: Option<&'static str>, + }, } /// Check if this enum can be safely exported based on the @@ -406,7 +401,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// representation which can be exported to C code). fn check_type_for_ffi(&self, cache: &mut FxHashSet>, - ty: Ty<'tcx>) -> FfiResult { + ty: Ty<'tcx>) -> FfiResult<'tcx> { use self::FfiResult::*; let cx = self.cx.tcx; @@ -422,23 +417,24 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match ty.sty { ty::TyAdt(def, substs) => { if def.is_phantom_data() { - return FfiPhantom; + return FfiPhantom(ty); } match def.adt_kind() { AdtKind::Struct => { if !def.repr.c() && !def.repr.transparent() { - return FfiUnsafe(FfiError { - message: "found struct without foreign-function-safe \ - representation annotation in foreign module", - help: Some("consider adding a #[repr(C)] attribute to the type"), - }); + return FfiUnsafe { + ty: ty, + reason: "this struct has unspecified layout", + help: Some("consider adding a #[repr(C)] attribute to this struct"), + }; } if def.non_enum_variant().fields.is_empty() { - return FfiUnsafe(FfiError { - message: "found zero-size struct in foreign module", + return FfiUnsafe { + ty: ty, + reason: "this struct has no fields", help: Some("consider adding a member to this struct"), - }); + }; } // We can't completely trust repr(C) and repr(transparent) markings; @@ -464,32 +460,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe => { all_phantom = false; } - FfiPhantom => {} - FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { + FfiPhantom(..) => {} + FfiUnsafe { .. } => { return r; } - FfiUnsafe(err) => { - return FfiBadStruct(def.did, err); - } } } - if all_phantom { FfiPhantom } else { FfiSafe } + if all_phantom { FfiPhantom(ty) } else { FfiSafe } } AdtKind::Union => { if !def.repr.c() { - return FfiUnsafe(FfiError { - message: "found union without foreign-function-safe \ - representation annotation in foreign module", - help: Some("consider adding a #[repr(C)] attribute to the type"), - }); + return FfiUnsafe { + ty: ty, + reason: "this union has unspecified layout", + help: Some("consider adding a #[repr(C)] attribute to this union"), + }; } if def.non_enum_variant().fields.is_empty() { - return FfiUnsafe(FfiError { - message: "found zero-size union in foreign module", - help: Some("consider adding a member to this union"), - }); + return FfiUnsafe { + ty: ty, + reason: "this union has no fields", + help: Some("consider adding a field to this union"), + }; } let mut all_phantom = true; @@ -502,17 +496,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe => { all_phantom = false; } - FfiPhantom => {} - FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { + FfiPhantom(..) => {} + FfiUnsafe { .. } => { return r; } - FfiUnsafe(err) => { - return FfiBadUnion(def.did, err); - } } } - if all_phantom { FfiPhantom } else { FfiSafe } + if all_phantom { FfiPhantom(ty) } else { FfiSafe } } AdtKind::Enum => { if def.variants.is_empty() { @@ -525,12 +516,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if !def.repr.c() && def.repr.int.is_none() { // Special-case types like `Option`. if !is_repr_nullable_ptr(cx, def, substs) { - return FfiUnsafe(FfiError { - message: "found enum without foreign-function-safe \ - representation annotation in foreign module", + return FfiUnsafe { + ty: ty, + reason: "enum has no representation hint", help: Some("consider adding a #[repr(...)] attribute \ - to the type"), - }); + to this enum"), + }; } } @@ -543,17 +534,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let r = self.check_type_for_ffi(cache, arg); match r { FfiSafe => {} - FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { + FfiUnsafe { .. } => { return r; } - FfiPhantom => { - return FfiBadEnum(def.did, FfiError { - message: "Found phantom data in enum variant", + FfiPhantom(..) => { + return FfiUnsafe { + ty: ty, + reason: "this enum contains a PhantomData field", help: None, - }); - } - FfiUnsafe(err) => { - return FfiBadEnum(def.did, err); + }; } } } @@ -563,59 +552,44 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - ty::TyChar => { - FfiUnsafe(FfiError { - message: "found Rust type `char` in foreign module", - help: Some("consider using `u32` or `libc::wchar_t`"), - }) - } + ty::TyChar => FfiUnsafe { + ty: ty, + reason: "the `char` type has no C equivalent", + help: Some("consider using `u32` or `libc::wchar_t` instead"), + }, - ty::TyInt(ast::IntTy::I128) => { - FfiUnsafe(FfiError { - message: "found Rust type `i128` in foreign module, but 128-bit \ - integers don't currently have a known stable ABI", - help: None, - }) - } - - ty::TyUint(ast::UintTy::U128) => { - FfiUnsafe(FfiError { - message: "found Rust type `u128` in foreign module, but 128-bit \ - integers don't currently have a known stable ABI", - help: None, - }) - } + ty::TyInt(ast::IntTy::I128) | ty::TyUint(ast::UintTy::U128) => FfiUnsafe { + ty: ty, + reason: "128-bit integers don't currently have a known stable ABI", + help: None, + }, // Primitive types with a stable representation. ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe, - ty::TySlice(_) => { - FfiUnsafe(FfiError { - message: "found Rust slice type in foreign module", - help: Some("consider using a raw pointer instead"), - }) - } - - ty::TyDynamic(..) => { - FfiUnsafe(FfiError { - message: "found Rust trait type in foreign module", - help: Some("consider using a raw pointer instead"), - }) - } - - ty::TyStr => { - FfiUnsafe(FfiError { - message: "found Rust type `str` in foreign module", - help: Some("consider using a `*const libc::c_char`"), - }) - } - - ty::TyTuple(..) => { - FfiUnsafe(FfiError { - message: "found Rust tuple type in foreign module", - help: Some("consider using a struct instead"), - }) - } + ty::TySlice(_) => FfiUnsafe { + ty: ty, + reason: "slices have no C equivalent", + help: Some("consider using a raw pointer instead"), + }, + + ty::TyDynamic(..) => FfiUnsafe { + ty: ty, + reason: "trait objects have no C equivalent", + help: Some("consider using a raw pointer instead"), + }, + + ty::TyStr => FfiUnsafe { + ty: ty, + reason: "string slices have no C equivalent", + help: Some("consider using `*const u8` and a length instead"), + }, + + ty::TyTuple(..) => FfiUnsafe { + ty: ty, + reason: "tuples have unspecified layout", + help: Some("consider using a struct instead"), + }, ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => self.check_type_for_ffi(cache, m.ty), @@ -625,11 +599,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::TyFnPtr(sig) => { match sig.abi() { Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => { - return FfiUnsafe(FfiError { - message: "found function pointer with Rust calling convention in \ - foreign module", - help: Some("consider using an `extern` function pointer"), - }) + return FfiUnsafe { + ty: ty, + reason: "this function pointer has Rust-specific calling convention", + help: Some("consider using an `fn \"extern\"(...) -> ...` \ + function pointer instead"), + } } _ => {} } @@ -677,48 +652,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match self.check_type_for_ffi(&mut FxHashSet(), ty) { FfiResult::FfiSafe => {} - FfiResult::FfiPhantom => { + FfiResult::FfiPhantom(ty) => { self.cx.span_lint(IMPROPER_CTYPES, sp, - &format!("found zero-sized type composed only \ - of phantom-data in a foreign-function.")); - } - FfiResult::FfiUnsafe(err) => { - let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, err.message); - if let Some(s) = err.help { - diag.help(s); - } - diag.emit(); - } - FfiResult::FfiBadStruct(_, err) => { - // FIXME: This diagnostic is difficult to read, and doesn't - // point at the relevant field. - let msg = format!("found non-foreign-function-safe member in struct \ - marked #[repr(C)]: {}", err.message); - let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg); - if let Some(s) = err.help { - diag.help(s); - } - diag.emit(); - } - FfiResult::FfiBadUnion(_, err) => { - // FIXME: This diagnostic is difficult to read, and doesn't - // point at the relevant field. - let msg = format!("found non-foreign-function-safe member in union \ - marked #[repr(C)]: {}", err.message); - let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg); - if let Some(s) = err.help { - diag.help(s); - } - diag.emit(); + &format!("`extern` block uses type `{}` which is not FFI-safe: \ + composed only of PhantomData", ty)); } - FfiResult::FfiBadEnum(_, err) => { - // FIXME: This diagnostic is difficult to read, and doesn't - // point at the relevant variant. - let msg = format!("found non-foreign-function-safe member in enum: {}", - err.message); + FfiResult::FfiUnsafe { ty: unsafe_ty, reason, help } => { + let msg = format!("`extern` block uses type `{}` which is not FFI-safe: {}", + unsafe_ty, reason); let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg); - if let Some(s) = err.help { + if let Some(s) = help { diag.help(s); } diag.emit(); diff --git a/src/test/compile-fail/issue-14309.rs b/src/test/compile-fail/issue-14309.rs index 56261c34a0308..f76fa3e4a8ece 100644 --- a/src/test/compile-fail/issue-14309.rs +++ b/src/test/compile-fail/issue-14309.rs @@ -37,13 +37,13 @@ struct D { } extern "C" { - fn foo(x: A); //~ ERROR found struct without foreign-function-safe - fn bar(x: B); //~ ERROR foreign-function-safe + fn foo(x: A); //~ ERROR type `A` which is not FFI-safe + fn bar(x: B); //~ ERROR type `A` fn baz(x: C); - fn qux(x: A2); //~ ERROR foreign-function-safe - fn quux(x: B2); //~ ERROR foreign-function-safe + fn qux(x: A2); //~ ERROR type `A` + fn quux(x: B2); //~ ERROR type `A` fn corge(x: C2); - fn fred(x: D); //~ ERROR foreign-function-safe + fn fred(x: D); //~ ERROR type `A` } fn main() { } diff --git a/src/test/compile-fail/issue-16250.rs b/src/test/compile-fail/issue-16250.rs index 288fe4a9abb82..f9d01003005e4 100644 --- a/src/test/compile-fail/issue-16250.rs +++ b/src/test/compile-fail/issue-16250.rs @@ -13,7 +13,7 @@ pub struct Foo; extern { - pub fn foo(x: (Foo)); //~ ERROR found struct without + pub fn foo(x: (Foo)); //~ ERROR unspecified layout } fn main() { diff --git a/src/test/compile-fail/lint-ctypes-enum.rs b/src/test/compile-fail/lint-ctypes-enum.rs index ce6afe1d219ea..7b7ffd8fc107c 100644 --- a/src/test/compile-fail/lint-ctypes-enum.rs +++ b/src/test/compile-fail/lint-ctypes-enum.rs @@ -27,9 +27,9 @@ enum Isize { A, B, C } extern { fn zf(x: Z); - fn uf(x: U); //~ ERROR found enum without foreign-function-safe - fn bf(x: B); //~ ERROR found enum without foreign-function-safe - fn tf(x: T); //~ ERROR found enum without foreign-function-safe + fn uf(x: U); //~ ERROR enum has no representation hint + fn bf(x: B); //~ ERROR enum has no representation hint + fn tf(x: T); //~ ERROR enum has no representation hint fn reprc(x: ReprC); fn u8(x: U8); fn isize(x: Isize); diff --git a/src/test/compile-fail/union/union-repr-c.rs b/src/test/compile-fail/union/union-repr-c.rs index 15a4197fe94c9..36c42ce1104e0 100644 --- a/src/test/compile-fail/union/union-repr-c.rs +++ b/src/test/compile-fail/union/union-repr-c.rs @@ -22,7 +22,7 @@ union W { extern "C" { static FOREIGN1: U; // OK - static FOREIGN2: W; //~ ERROR found union without foreign-function-safe representation + static FOREIGN2: W; //~ ERROR union has unspecified layout } fn main() {} diff --git a/src/test/ui/lint-ctypes.rs b/src/test/ui/lint-ctypes.rs index c22239dee0a80..77cb1ef0f5130 100644 --- a/src/test/ui/lint-ctypes.rs +++ b/src/test/ui/lint-ctypes.rs @@ -51,27 +51,27 @@ pub struct TransparentCustomZst(i32, ZeroSize); pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); extern { - pub fn ptr_type1(size: *const Foo); //~ ERROR: found struct without - pub fn ptr_type2(size: *const Foo); //~ ERROR: found struct without - pub fn slice_type(p: &[u32]); //~ ERROR: found Rust slice type - pub fn str_type(p: &str); //~ ERROR: found Rust type - pub fn box_type(p: Box); //~ ERROR found struct without - pub fn char_type(p: char); //~ ERROR found Rust type - pub fn i128_type(p: i128); //~ ERROR found Rust type - pub fn u128_type(p: u128); //~ ERROR found Rust type - pub fn trait_type(p: &Clone); //~ ERROR found Rust trait type - pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type - pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type - pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct - pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR found zero-sized type + pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo` + pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo` + pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]` + pub fn str_type(p: &str); //~ ERROR: uses type `str` + pub fn box_type(p: Box); //~ ERROR uses type `std::boxed::Box` + pub fn char_type(p: char); //~ ERROR uses type `char` + pub fn i128_type(p: i128); //~ ERROR uses type `i128` + pub fn u128_type(p: u128); //~ ERROR uses type `u128` + pub fn trait_type(p: &Clone); //~ ERROR uses type `std::clone::Clone` + pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)` + pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)` + pub fn zero_size(p: ZeroSize); //~ ERROR struct has no fields + pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR composed only of PhantomData pub fn zero_size_phantom_toplevel() - -> ::std::marker::PhantomData; //~ ERROR: found zero-sized type - pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust - pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust - pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without - pub fn transparent_i128(p: TransparentI128); //~ ERROR: found Rust type `i128` - pub fn transparent_str(p: TransparentStr); //~ ERROR: found Rust type `str` - pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: found struct without + -> ::std::marker::PhantomData; //~ ERROR: composed only of PhantomData + pub fn fn_type(p: RustFn); //~ ERROR function pointer has Rust-specific + pub fn fn_type2(p: fn()); //~ ERROR function pointer has Rust-specific + pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box` + pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128` + pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str` + pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box` pub fn good3(fptr: Option); pub fn good4(aptr: &[u8; 4 as usize]); diff --git a/src/test/ui/lint-ctypes.stderr b/src/test/ui/lint-ctypes.stderr index 0f7f7e048e3ab..8ecdae07a5329 100644 --- a/src/test/ui/lint-ctypes.stderr +++ b/src/test/ui/lint-ctypes.stderr @@ -1,7 +1,7 @@ -error: found struct without foreign-function-safe representation annotation in foreign module +error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout --> $DIR/lint-ctypes.rs:54:28 | -54 | pub fn ptr_type1(size: *const Foo); //~ ERROR: found struct without +54 | pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo` | ^^^^^^^^^^ | note: lint level defined here @@ -9,149 +9,149 @@ note: lint level defined here | 11 | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ - = help: consider adding a #[repr(C)] attribute to the type + = help: consider adding a #[repr(C)] attribute to this struct -error: found struct without foreign-function-safe representation annotation in foreign module +error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout --> $DIR/lint-ctypes.rs:55:28 | -55 | pub fn ptr_type2(size: *const Foo); //~ ERROR: found struct without +55 | pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo` | ^^^^^^^^^^ | - = help: consider adding a #[repr(C)] attribute to the type + = help: consider adding a #[repr(C)] attribute to this struct -error: found Rust slice type in foreign module +error: `extern` block uses type `[u32]` which is not FFI-safe: slices have no C equivalent --> $DIR/lint-ctypes.rs:56:26 | -56 | pub fn slice_type(p: &[u32]); //~ ERROR: found Rust slice type +56 | pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]` | ^^^^^^ | = help: consider using a raw pointer instead -error: found Rust type `str` in foreign module +error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent --> $DIR/lint-ctypes.rs:57:24 | -57 | pub fn str_type(p: &str); //~ ERROR: found Rust type +57 | pub fn str_type(p: &str); //~ ERROR: uses type `str` | ^^^^ | - = help: consider using a `*const libc::c_char` + = help: consider using `*const u8` and a length instead -error: found struct without foreign-function-safe representation annotation in foreign module +error: `extern` block uses type `std::boxed::Box` which is not FFI-safe: this struct has unspecified layout --> $DIR/lint-ctypes.rs:58:24 | -58 | pub fn box_type(p: Box); //~ ERROR found struct without +58 | pub fn box_type(p: Box); //~ ERROR uses type `std::boxed::Box` | ^^^^^^^^ | - = help: consider adding a #[repr(C)] attribute to the type + = help: consider adding a #[repr(C)] attribute to this struct -error: found Rust type `char` in foreign module +error: `extern` block uses type `char` which is not FFI-safe: the `char` type has no C equivalent --> $DIR/lint-ctypes.rs:59:25 | -59 | pub fn char_type(p: char); //~ ERROR found Rust type +59 | pub fn char_type(p: char); //~ ERROR uses type `char` | ^^^^ | - = help: consider using `u32` or `libc::wchar_t` + = help: consider using `u32` or `libc::wchar_t` instead -error: found Rust type `i128` in foreign module, but 128-bit integers don't currently have a known stable ABI +error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI --> $DIR/lint-ctypes.rs:60:25 | -60 | pub fn i128_type(p: i128); //~ ERROR found Rust type +60 | pub fn i128_type(p: i128); //~ ERROR uses type `i128` | ^^^^ -error: found Rust type `u128` in foreign module, but 128-bit integers don't currently have a known stable ABI +error: `extern` block uses type `u128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI --> $DIR/lint-ctypes.rs:61:25 | -61 | pub fn u128_type(p: u128); //~ ERROR found Rust type +61 | pub fn u128_type(p: u128); //~ ERROR uses type `u128` | ^^^^ -error: found Rust trait type in foreign module +error: `extern` block uses type `std::clone::Clone` which is not FFI-safe: trait objects have no C equivalent --> $DIR/lint-ctypes.rs:62:26 | -62 | pub fn trait_type(p: &Clone); //~ ERROR found Rust trait type +62 | pub fn trait_type(p: &Clone); //~ ERROR uses type `std::clone::Clone` | ^^^^^^ | = help: consider using a raw pointer instead -error: found Rust tuple type in foreign module +error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout --> $DIR/lint-ctypes.rs:63:26 | -63 | pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type +63 | pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)` | ^^^^^^^^^^ | = help: consider using a struct instead -error: found Rust tuple type in foreign module +error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout --> $DIR/lint-ctypes.rs:64:27 | -64 | pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type +64 | pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)` | ^^^^^^^ | = help: consider using a struct instead -error: found zero-size struct in foreign module +error: `extern` block uses type `ZeroSize` which is not FFI-safe: this struct has no fields --> $DIR/lint-ctypes.rs:65:25 | -65 | pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct +65 | pub fn zero_size(p: ZeroSize); //~ ERROR struct has no fields | ^^^^^^^^ | = help: consider adding a member to this struct -error: found zero-sized type composed only of phantom-data in a foreign-function. +error: `extern` block uses type `ZeroSizeWithPhantomData` which is not FFI-safe: composed only of PhantomData --> $DIR/lint-ctypes.rs:66:33 | -66 | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR found zero-sized type +66 | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR composed only of PhantomData | ^^^^^^^^^^^^^^^^^^^^^^^ -error: found zero-sized type composed only of phantom-data in a foreign-function. +error: `extern` block uses type `std::marker::PhantomData` which is not FFI-safe: composed only of PhantomData --> $DIR/lint-ctypes.rs:68:12 | -68 | -> ::std::marker::PhantomData; //~ ERROR: found zero-sized type +68 | -> ::std::marker::PhantomData; //~ ERROR: composed only of PhantomData | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: found function pointer with Rust calling convention in foreign module +error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention --> $DIR/lint-ctypes.rs:69:23 | -69 | pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust +69 | pub fn fn_type(p: RustFn); //~ ERROR function pointer has Rust-specific | ^^^^^^ | - = help: consider using an `extern` function pointer + = help: consider using an `fn "extern"(...) -> ...` function pointer instead -error: found function pointer with Rust calling convention in foreign module +error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention --> $DIR/lint-ctypes.rs:70:24 | -70 | pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust +70 | pub fn fn_type2(p: fn()); //~ ERROR function pointer has Rust-specific | ^^^^ | - = help: consider using an `extern` function pointer + = help: consider using an `fn "extern"(...) -> ...` function pointer instead -error: found struct without foreign-function-safe representation annotation in foreign module +error: `extern` block uses type `std::boxed::Box` which is not FFI-safe: this struct has unspecified layout --> $DIR/lint-ctypes.rs:71:28 | -71 | pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without +71 | pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box` | ^^^^^^^^^^ | - = help: consider adding a #[repr(C)] attribute to the type + = help: consider adding a #[repr(C)] attribute to this struct -error: found non-foreign-function-safe member in struct marked #[repr(C)]: found Rust type `i128` in foreign module, but 128-bit integers don't currently have a known stable ABI +error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI --> $DIR/lint-ctypes.rs:72:32 | -72 | pub fn transparent_i128(p: TransparentI128); //~ ERROR: found Rust type `i128` +72 | pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128` | ^^^^^^^^^^^^^^^ -error: found non-foreign-function-safe member in struct marked #[repr(C)]: found Rust type `str` in foreign module +error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent --> $DIR/lint-ctypes.rs:73:31 | -73 | pub fn transparent_str(p: TransparentStr); //~ ERROR: found Rust type `str` +73 | pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str` | ^^^^^^^^^^^^^^ | - = help: consider using a `*const libc::c_char` + = help: consider using `*const u8` and a length instead -error: found non-foreign-function-safe member in struct marked #[repr(C)]: found struct without foreign-function-safe representation annotation in foreign module +error: `extern` block uses type `std::boxed::Box` which is not FFI-safe: this struct has unspecified layout --> $DIR/lint-ctypes.rs:74:30 | -74 | pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: found struct without +74 | pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box` | ^^^^^^^^^^^^^^^^ | - = help: consider adding a #[repr(C)] attribute to the type + = help: consider adding a #[repr(C)] attribute to this struct error: aborting due to 20 previous errors From 22a171609bf555833b277a70420a24696dd8805d Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Wed, 14 Feb 2018 19:39:04 +0100 Subject: [PATCH 25/42] [improper_ctypes] Suggest repr(transparent) for structs The suggestion is unconditional, so following it could lead to further errors. This is already the case for the repr(C) suggestion, which makes this acceptable, though not *good*. Checking up-front whether the suggestion can help would be great but applies more broadly (and would require some refactoring to avoid duplicating the checks). --- src/librustc_lint/types.rs | 3 ++- src/test/ui/lint-ctypes.stderr | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 7203b1b1e7dae..378fe99bf3150 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -425,7 +425,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiUnsafe { ty: ty, reason: "this struct has unspecified layout", - help: Some("consider adding a #[repr(C)] attribute to this struct"), + help: Some("consider adding a #[repr(C)] or #[repr(transparent)] \ + attribute to this struct"), }; } diff --git a/src/test/ui/lint-ctypes.stderr b/src/test/ui/lint-ctypes.stderr index 8ecdae07a5329..a8628c8b3d2d9 100644 --- a/src/test/ui/lint-ctypes.stderr +++ b/src/test/ui/lint-ctypes.stderr @@ -9,7 +9,7 @@ note: lint level defined here | 11 | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ - = help: consider adding a #[repr(C)] attribute to this struct + = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout --> $DIR/lint-ctypes.rs:55:28 @@ -17,7 +17,7 @@ error: `extern` block uses type `Foo` which is not FFI-safe: this struct has uns 55 | pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo` | ^^^^^^^^^^ | - = help: consider adding a #[repr(C)] attribute to this struct + = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct error: `extern` block uses type `[u32]` which is not FFI-safe: slices have no C equivalent --> $DIR/lint-ctypes.rs:56:26 @@ -41,7 +41,7 @@ error: `extern` block uses type `std::boxed::Box` which is not FFI-safe: th 58 | pub fn box_type(p: Box); //~ ERROR uses type `std::boxed::Box` | ^^^^^^^^ | - = help: consider adding a #[repr(C)] attribute to this struct + = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct error: `extern` block uses type `char` which is not FFI-safe: the `char` type has no C equivalent --> $DIR/lint-ctypes.rs:59:25 @@ -129,7 +129,7 @@ error: `extern` block uses type `std::boxed::Box` which is not FFI-safe: th 71 | pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box` | ^^^^^^^^^^ | - = help: consider adding a #[repr(C)] attribute to this struct + = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI --> $DIR/lint-ctypes.rs:72:32 @@ -151,7 +151,7 @@ error: `extern` block uses type `std::boxed::Box` which is not FFI-safe: th 74 | pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box` | ^^^^^^^^^^^^^^^^ | - = help: consider adding a #[repr(C)] attribute to this struct + = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct error: aborting due to 20 previous errors From 9d493c897b4382dc145b9448b3fafdfbbaecf528 Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Wed, 14 Feb 2018 23:01:39 +0100 Subject: [PATCH 26/42] [improper_ctypes] Point at definition of non-FFI-safe type if possible --- src/librustc_lint/types.rs | 5 +++++ src/test/ui/lint-ctypes.stderr | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 378fe99bf3150..396d830d85be1 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -666,6 +666,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if let Some(s) = help { diag.help(s); } + if let ty::TyAdt(def, _) = unsafe_ty.sty { + if let Some(sp) = self.cx.tcx.hir.span_if_local(def.did) { + diag.span_note(sp, "type defined here"); + } + } diag.emit(); } } diff --git a/src/test/ui/lint-ctypes.stderr b/src/test/ui/lint-ctypes.stderr index a8628c8b3d2d9..2abf08d55f7b3 100644 --- a/src/test/ui/lint-ctypes.stderr +++ b/src/test/ui/lint-ctypes.stderr @@ -10,6 +10,11 @@ note: lint level defined here 11 | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct +note: type defined here + --> $DIR/lint-ctypes.rs:32:1 + | +32 | pub struct Foo; + | ^^^^^^^^^^^^^^^ error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout --> $DIR/lint-ctypes.rs:55:28 @@ -18,6 +23,11 @@ error: `extern` block uses type `Foo` which is not FFI-safe: this struct has uns | ^^^^^^^^^^ | = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct +note: type defined here + --> $DIR/lint-ctypes.rs:32:1 + | +32 | pub struct Foo; + | ^^^^^^^^^^^^^^^ error: `extern` block uses type `[u32]` which is not FFI-safe: slices have no C equivalent --> $DIR/lint-ctypes.rs:56:26 @@ -94,6 +104,11 @@ error: `extern` block uses type `ZeroSize` which is not FFI-safe: this struct ha | ^^^^^^^^ | = help: consider adding a member to this struct +note: type defined here + --> $DIR/lint-ctypes.rs:28:1 + | +28 | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData` which is not FFI-safe: composed only of PhantomData --> $DIR/lint-ctypes.rs:66:33 From 051ea5cc9bac00c7f588b4eae72b8a382d8ceb68 Mon Sep 17 00:00:00 2001 From: Robin Kruppe Date: Wed, 14 Feb 2018 23:11:29 +0100 Subject: [PATCH 27/42] [improper_ctypes] Don't suggest raw pointers when encountering trait objects It's unhelpful since raw pointers to trait objects are also FFI-unsafe and casting to a thin raw pointer loses the vtable. There are working solutions that _involve_ raw pointers but they're too complex to explain in one line and have serious trade offs. --- src/librustc_lint/types.rs | 2 +- src/test/ui/lint-ctypes.stderr | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 396d830d85be1..ef9b3d38c637c 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -577,7 +577,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::TyDynamic(..) => FfiUnsafe { ty: ty, reason: "trait objects have no C equivalent", - help: Some("consider using a raw pointer instead"), + help: None, }, ty::TyStr => FfiUnsafe { diff --git a/src/test/ui/lint-ctypes.stderr b/src/test/ui/lint-ctypes.stderr index 2abf08d55f7b3..748c311055fa9 100644 --- a/src/test/ui/lint-ctypes.stderr +++ b/src/test/ui/lint-ctypes.stderr @@ -78,8 +78,6 @@ error: `extern` block uses type `std::clone::Clone` which is not FFI-safe: trait | 62 | pub fn trait_type(p: &Clone); //~ ERROR uses type `std::clone::Clone` | ^^^^^^ - | - = help: consider using a raw pointer instead error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout --> $DIR/lint-ctypes.rs:63:26 From 10fbdb832b2ad5807b89a005f9d4bccaec231fb1 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Fri, 16 Feb 2018 16:45:37 +1300 Subject: [PATCH 28/42] save-analysis: power through bracket mis-counts Closes #47981 This is pretty unsatisfying since it is working around a span bug. However, I can't track down the span bug and it could be in the parser, proc macro expansion, the user macro, or Syn (or any other library that can manipulate spans). Given that user code can cause this error, I think we need to be more robust here. --- src/librustc_save_analysis/span_utils.rs | 26 +++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/librustc_save_analysis/span_utils.rs b/src/librustc_save_analysis/span_utils.rs index 25e81e6f326e1..d5a58c08cbe5a 100644 --- a/src/librustc_save_analysis/span_utils.rs +++ b/src/librustc_save_analysis/span_utils.rs @@ -115,7 +115,7 @@ impl<'a> SpanUtils<'a> { // We keep track of the following two counts - the depth of nesting of // angle brackets, and the depth of nesting of square brackets. For the // angle bracket count, we only count tokens which occur outside of any - // square brackets (i.e. bracket_count == 0). The intutition here is + // square brackets (i.e. bracket_count == 0). The intuition here is // that we want to count angle brackets in the type, but not any which // could be in expression context (because these could mean 'less than', // etc.). @@ -151,18 +151,20 @@ impl<'a> SpanUtils<'a> { } prev = next; } - if angle_count != 0 || bracket_count != 0 { - let loc = self.sess.codemap().lookup_char_pos(span.lo()); - span_bug!( - span, - "Mis-counted brackets when breaking path? Parsing '{}' \ - in {}, line {}", - self.snippet(span), - loc.file.name, - loc.line - ); + #[cfg(debug_assertions)] { + if angle_count != 0 || bracket_count != 0 { + let loc = self.sess.codemap().lookup_char_pos(span.lo()); + span_bug!( + span, + "Mis-counted brackets when breaking path? Parsing '{}' \ + in {}, line {}", + self.snippet(span), + loc.file.name, + loc.line + ); + } } - if result.is_none() && prev.tok.is_ident() && angle_count == 0 { + if result.is_none() && prev.tok.is_ident() { return Some(prev.sp); } result From 0be2dc8d9b4765e59cf9bbf3d342de00fa1b9aec Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Sat, 17 Feb 2018 14:18:53 +0000 Subject: [PATCH 29/42] fix stderr --- src/test/ui/inference-variable-behind-raw-pointer.stderr | 2 +- src/test/ui/span/issue-42234-unknown-receiver-type.stderr | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/ui/inference-variable-behind-raw-pointer.stderr b/src/test/ui/inference-variable-behind-raw-pointer.stderr index bb1d921f1c61c..d0ee55c092b28 100644 --- a/src/test/ui/inference-variable-behind-raw-pointer.stderr +++ b/src/test/ui/inference-variable-behind-raw-pointer.stderr @@ -1,4 +1,4 @@ -warning: type annotations needed +warning: the type of this value must be known in this context --> $DIR/inference-variable-behind-raw-pointer.rs:18:13 | 18 | if data.is_null() {} diff --git a/src/test/ui/span/issue-42234-unknown-receiver-type.stderr b/src/test/ui/span/issue-42234-unknown-receiver-type.stderr index 2a85e1f1c45ba..d87cec642f18e 100644 --- a/src/test/ui/span/issue-42234-unknown-receiver-type.stderr +++ b/src/test/ui/span/issue-42234-unknown-receiver-type.stderr @@ -1,15 +1,17 @@ error[E0282]: type annotations needed --> $DIR/issue-42234-unknown-receiver-type.rs:17:5 | +16 | let x: Option<_> = None; + | - consider giving `x` a type 17 | x.unwrap().method_that_could_exist_on_some_type(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ cannot infer type for `T` error[E0282]: type annotations needed --> $DIR/issue-42234-unknown-receiver-type.rs:22:5 | 22 | / data.iter() //~ ERROR 22:5: 23:20: type annotations needed 23 | | .sum::<_>() - | |___________________^ + | |___________________^ cannot infer type for `_` error: aborting due to 2 previous errors From b3d6597855d290d53f4f7bc80a5da07d5a5d5193 Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Fri, 9 Feb 2018 14:43:21 -0600 Subject: [PATCH 30/42] move manual "extern crate" statements outside auto "fn main" in doctests --- src/librustdoc/test.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 08258489a2ec2..f811eea0f8771 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -417,7 +417,8 @@ fn partition_source(s: &str) -> (String, String) { for line in s.lines() { let trimline = line.trim(); let header = trimline.is_whitespace() || - trimline.starts_with("#!["); + trimline.starts_with("#![") || + trimline.starts_with("extern crate"); if !header || after_header { after_header = true; after.push_str(line); @@ -858,8 +859,8 @@ use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] -fn main() { extern crate asdf; +fn main() { use asdf::qwop; assert_eq!(2+2, 4); }".to_string(); From d8d4c583be96d9eb654891288829af9410b68e1c Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Mon, 12 Feb 2018 10:32:04 -0600 Subject: [PATCH 31/42] fix E0260 error index doctest --- src/librustc_resolve/diagnostics.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 8a29155d12d5b..c1e6b4f5a17d9 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -325,6 +325,8 @@ Erroneous code example: extern crate core; struct core; + +fn main() {} ``` There are two possible solutions: From 472dcdb4ecb3fbea4b3e2b1d5952adc1d0f6cc76 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sat, 17 Feb 2018 20:57:00 -0500 Subject: [PATCH 32/42] Fix broken documentation link. --- src/libstd/sys/unix/ext/net.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 86b0f35be924d..31bdc5ea1f565 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -415,7 +415,7 @@ impl UnixStream { /// method. /// /// [`None`]: ../../../../std/option/enum.Option.html#variant.None - /// [`read`]: ../../../../std/io/trait.Write.html#tymethod.write + /// [`write`]: ../../../../std/io/trait.Write.html#tymethod.write /// [`Duration`]: ../../../../std/time/struct.Duration.html /// /// # Examples From 4370a5877c6c606dec106d200ccb36d5d78082a5 Mon Sep 17 00:00:00 2001 From: csmoe <35686186+csmoe@users.noreply.github.com> Date: Sun, 18 Feb 2018 11:07:52 +0800 Subject: [PATCH 33/42] fix tyvar_behind_raw_pointer error code --- src/librustc_typeck/check/method/probe.rs | 2 +- src/test/ui/inference-variable-behind-raw-pointer.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index e8c3966f23f08..9f3e44f56dae2 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -337,7 +337,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { lint::builtin::TYVAR_BEHIND_RAW_POINTER, scope_expr_id, span, - &format!("the type of this value must be known in this context")); + &format!("type annotations needed")); } } else { let t = self.structurally_resolved_type(span, final_ty); diff --git a/src/test/ui/inference-variable-behind-raw-pointer.stderr b/src/test/ui/inference-variable-behind-raw-pointer.stderr index d0ee55c092b28..bb1d921f1c61c 100644 --- a/src/test/ui/inference-variable-behind-raw-pointer.stderr +++ b/src/test/ui/inference-variable-behind-raw-pointer.stderr @@ -1,4 +1,4 @@ -warning: the type of this value must be known in this context +warning: type annotations needed --> $DIR/inference-variable-behind-raw-pointer.rs:18:13 | 18 | if data.is_null() {} From ba8d6d18d0378a515ed692f7e7fdb0c5a068c7ea Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 8 Feb 2018 15:48:07 -0800 Subject: [PATCH 34/42] Fix span bug. --- src/libsyntax/parse/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ac582627f88fd..b21813accf911 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2121,8 +2121,8 @@ impl<'a> Parser<'a> { // Check if a colon exists one ahead. This means we're parsing a fieldname. let (fieldname, expr, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { let fieldname = self.parse_field_name()?; - self.bump(); hi = self.prev_span; + self.bump(); (fieldname, self.parse_expr()?, false) } else { let fieldname = self.parse_ident_common(false)?; From 4294528b5da1dabd777aa88f621f222f0b0df8f4 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 8 Feb 2018 15:48:16 -0800 Subject: [PATCH 35/42] Improve hygiene when privacy-checking struct expression or struct constructor fields. --- src/librustc_privacy/lib.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index b46882f054df9..f8221698231dc 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -34,6 +34,7 @@ use rustc::util::nodemap::NodeSet; use syntax::ast::{self, CRATE_NODE_ID, Ident}; use syntax::symbol::keywords; use syntax_pos::Span; +use syntax_pos::hygiene::SyntaxContext; use std::cmp; use std::mem::replace; @@ -491,9 +492,13 @@ struct NamePrivacyVisitor<'a, 'tcx: 'a> { } impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { - // Checks that a field is accessible. - fn check_field(&mut self, span: Span, def: &'tcx ty::AdtDef, field: &'tcx ty::FieldDef) { - let ident = Ident { ctxt: span.ctxt().modern(), ..keywords::Invalid.ident() }; + // Checks that a field in a struct constructor (expression or pattern) is accessible. + fn check_field(&mut self, + use_ctxt: SyntaxContext, // Syntax context of the field name at the use site + span: Span, // Span of the field pattern, e.g. `x: 0` + def: &'tcx ty::AdtDef, // Definition of the struct or enum + field: &'tcx ty::FieldDef) { // Definition of the field + let ident = Ident { ctxt: use_ctxt.modern(), ..keywords::Invalid.ident() }; let def_id = self.tcx.adjust_ident(ident, def.did, self.current_item).1; if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) { struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private", @@ -566,12 +571,17 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { // unmentioned fields, just check them all. for variant_field in &variant.fields { let field = fields.iter().find(|f| f.name.node == variant_field.name); - let span = if let Some(f) = field { f.span } else { base.span }; - self.check_field(span, adt, variant_field); + let (use_ctxt, span) = match field { + Some(field) => (field.name.node.to_ident().ctxt, field.span), + None => (base.span.ctxt(), base.span), + }; + self.check_field(use_ctxt, span, adt, variant_field); } } else { for field in fields { - self.check_field(field.span, adt, variant.field_named(field.name.node)); + let use_ctxt = field.name.node.to_ident().ctxt; + let field_def = variant.field_named(field.name.node); + self.check_field(use_ctxt, field.span, adt, field_def); } } } @@ -588,7 +598,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap(); let variant = adt.variant_of_def(def); for field in fields { - self.check_field(field.span, adt, variant.field_named(field.node.name)); + let use_ctxt = field.node.name.to_ident().ctxt; + let field_def = variant.field_named(field.node.name); + self.check_field(use_ctxt, field.span, adt, field_def); } } _ => {} From 876c42dad68a17bee609906145b85815a02342d5 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 8 Feb 2018 15:55:35 -0800 Subject: [PATCH 36/42] Add test. --- src/test/run-pass/hygiene/issue-47311.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/test/run-pass/hygiene/issue-47311.rs diff --git a/src/test/run-pass/hygiene/issue-47311.rs b/src/test/run-pass/hygiene/issue-47311.rs new file mode 100644 index 0000000000000..d68d8fc8c7481 --- /dev/null +++ b/src/test/run-pass/hygiene/issue-47311.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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(decl_macro)] +#![allow(unused)] + +macro m($S:ident, $x:ident) { + $S { $x: 0 } +} + +mod foo { + struct S { x: i32 } + + fn f() { ::m!(S, x); } +} + +fn main() {} From 169643d7387daa6353149913536fc7cdfb974b55 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 17 Feb 2018 19:35:59 -0800 Subject: [PATCH 37/42] Update tests. --- .../did_you_mean/issue-42599_available_fields_note.stderr | 4 ++-- src/test/ui/error-codes/E0062.stderr | 2 +- src/test/ui/error-codes/E0559.stderr | 2 +- src/test/ui/error-codes/E0560.stderr | 2 +- src/test/ui/issue-19922.stderr | 2 +- src/test/ui/numeric-fields.stderr | 2 +- src/test/ui/struct-fields-hints-no-dupe.stderr | 2 +- src/test/ui/struct-fields-hints.stderr | 2 +- src/test/ui/struct-fields-too-many.stderr | 2 +- src/test/ui/suggest-private-fields.stderr | 8 ++++---- src/test/ui/union/union-fields-2.stderr | 2 +- src/test/ui/union/union-suggest-field.stderr | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr b/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr index d5dcef638471e..2c7701e9965db 100644 --- a/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr +++ b/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr @@ -2,13 +2,13 @@ error[E0560]: struct `submodule::Demo` has no field named `inocently_mispellable --> $DIR/issue-42599_available_fields_note.rs:26:39 | 26 | Self { secret_integer: 2, inocently_mispellable: () } - | ^^^^^^^^^^^^^^^^^^^^^^ field does not exist - did you mean `innocently_misspellable`? + | ^^^^^^^^^^^^^^^^^^^^^ field does not exist - did you mean `innocently_misspellable`? error[E0560]: struct `submodule::Demo` has no field named `egregiously_nonexistent_field` --> $DIR/issue-42599_available_fields_note.rs:31:39 | 31 | Self { secret_integer: 3, egregiously_nonexistent_field: () } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `submodule::Demo` does not have this field + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `submodule::Demo` does not have this field | = note: available fields are: `favorite_integer`, `secret_integer`, `innocently_misspellable`, `another_field`, `yet_another_field` ... and 2 others diff --git a/src/test/ui/error-codes/E0062.stderr b/src/test/ui/error-codes/E0062.stderr index 6c5ecf48045b1..462ee2ac50d83 100644 --- a/src/test/ui/error-codes/E0062.stderr +++ b/src/test/ui/error-codes/E0062.stderr @@ -4,7 +4,7 @@ error[E0062]: field `x` specified more than once 17 | x: 0, | ---- first use of `x` 18 | x: 0, - | ^^ used more than once + | ^ used more than once error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0559.stderr b/src/test/ui/error-codes/E0559.stderr index 5d145a9518095..0bdf104ec6b2d 100644 --- a/src/test/ui/error-codes/E0559.stderr +++ b/src/test/ui/error-codes/E0559.stderr @@ -2,7 +2,7 @@ error[E0559]: variant `Field::Fool` has no field named `joke` --> $DIR/E0559.rs:16:27 | 16 | let s = Field::Fool { joke: 0 }; - | ^^^^^ `Field::Fool` does not have this field + | ^^^^ `Field::Fool` does not have this field | = note: available fields are: `x` diff --git a/src/test/ui/error-codes/E0560.stderr b/src/test/ui/error-codes/E0560.stderr index a0185aa8af64b..aedd2d59142a1 100644 --- a/src/test/ui/error-codes/E0560.stderr +++ b/src/test/ui/error-codes/E0560.stderr @@ -2,7 +2,7 @@ error[E0560]: struct `Simba` has no field named `father` --> $DIR/E0560.rs:16:32 | 16 | let s = Simba { mother: 1, father: 0 }; - | ^^^^^^^ `Simba` does not have this field + | ^^^^^^ `Simba` does not have this field | = note: available fields are: `mother` diff --git a/src/test/ui/issue-19922.stderr b/src/test/ui/issue-19922.stderr index f96392441967f..035901abac668 100644 --- a/src/test/ui/issue-19922.stderr +++ b/src/test/ui/issue-19922.stderr @@ -2,7 +2,7 @@ error[E0559]: variant `Homura::Akemi` has no field named `kaname` --> $DIR/issue-19922.rs:16:34 | 16 | let homura = Homura::Akemi { kaname: () }; - | ^^^^^^^ `Homura::Akemi` does not have this field + | ^^^^^^ `Homura::Akemi` does not have this field | = note: available fields are: `madoka` diff --git a/src/test/ui/numeric-fields.stderr b/src/test/ui/numeric-fields.stderr index cdf85d4f9718e..8261e9034a69b 100644 --- a/src/test/ui/numeric-fields.stderr +++ b/src/test/ui/numeric-fields.stderr @@ -2,7 +2,7 @@ error[E0560]: struct `S` has no field named `0b1` --> $DIR/numeric-fields.rs:14:15 | 14 | let s = S{0b1: 10, 0: 11}; - | ^^^^ `S` does not have this field + | ^^^ `S` does not have this field | = note: available fields are: `0`, `1` diff --git a/src/test/ui/struct-fields-hints-no-dupe.stderr b/src/test/ui/struct-fields-hints-no-dupe.stderr index 93cbe1f5afa80..0de22d1a84f7f 100644 --- a/src/test/ui/struct-fields-hints-no-dupe.stderr +++ b/src/test/ui/struct-fields-hints-no-dupe.stderr @@ -2,7 +2,7 @@ error[E0560]: struct `A` has no field named `bar` --> $DIR/struct-fields-hints-no-dupe.rs:20:9 | 20 | bar : 42, - | ^^^^^ field does not exist - did you mean `barr`? + | ^^^ field does not exist - did you mean `barr`? error: aborting due to previous error diff --git a/src/test/ui/struct-fields-hints.stderr b/src/test/ui/struct-fields-hints.stderr index a7c77103e7357..f602f398acd1c 100644 --- a/src/test/ui/struct-fields-hints.stderr +++ b/src/test/ui/struct-fields-hints.stderr @@ -2,7 +2,7 @@ error[E0560]: struct `A` has no field named `bar` --> $DIR/struct-fields-hints.rs:20:9 | 20 | bar : 42, - | ^^^^^ field does not exist - did you mean `car`? + | ^^^ field does not exist - did you mean `car`? error: aborting due to previous error diff --git a/src/test/ui/struct-fields-too-many.stderr b/src/test/ui/struct-fields-too-many.stderr index ec353d00aa7ee..d61dd8da6ece3 100644 --- a/src/test/ui/struct-fields-too-many.stderr +++ b/src/test/ui/struct-fields-too-many.stderr @@ -2,7 +2,7 @@ error[E0560]: struct `BuildData` has no field named `bar` --> $DIR/struct-fields-too-many.rs:18:9 | 18 | bar: 0 - | ^^^^ `BuildData` does not have this field + | ^^^ `BuildData` does not have this field | = note: available fields are: `foo` diff --git a/src/test/ui/suggest-private-fields.stderr b/src/test/ui/suggest-private-fields.stderr index d32d85f6e3fd1..a451f9c8fe7d2 100644 --- a/src/test/ui/suggest-private-fields.stderr +++ b/src/test/ui/suggest-private-fields.stderr @@ -2,13 +2,13 @@ error[E0560]: struct `xc::B` has no field named `aa` --> $DIR/suggest-private-fields.rs:25:9 | 25 | aa: 20, - | ^^^ field does not exist - did you mean `a`? + | ^^ field does not exist - did you mean `a`? error[E0560]: struct `xc::B` has no field named `bb` --> $DIR/suggest-private-fields.rs:27:9 | 27 | bb: 20, - | ^^^ `xc::B` does not have this field + | ^^ `xc::B` does not have this field | = note: available fields are: `a` @@ -16,13 +16,13 @@ error[E0560]: struct `A` has no field named `aa` --> $DIR/suggest-private-fields.rs:32:9 | 32 | aa: 20, - | ^^^ field does not exist - did you mean `a`? + | ^^ field does not exist - did you mean `a`? error[E0560]: struct `A` has no field named `bb` --> $DIR/suggest-private-fields.rs:34:9 | 34 | bb: 20, - | ^^^ field does not exist - did you mean `b`? + | ^^ field does not exist - did you mean `b`? error: aborting due to 4 previous errors diff --git a/src/test/ui/union/union-fields-2.stderr b/src/test/ui/union/union-fields-2.stderr index f6c64dcabd74c..d66ebaa2187eb 100644 --- a/src/test/ui/union/union-fields-2.stderr +++ b/src/test/ui/union/union-fields-2.stderr @@ -14,7 +14,7 @@ error[E0560]: union `U` has no field named `c` --> $DIR/union-fields-2.rs:20:29 | 20 | let u = U { a: 0, b: 1, c: 2 }; //~ ERROR union expressions should have exactly one field - | ^^ `U` does not have this field + | ^ `U` does not have this field | = note: available fields are: `a`, `b` diff --git a/src/test/ui/union/union-suggest-field.stderr b/src/test/ui/union/union-suggest-field.stderr index d2ea09553bcc1..e3cb52270ddd6 100644 --- a/src/test/ui/union/union-suggest-field.stderr +++ b/src/test/ui/union/union-suggest-field.stderr @@ -2,7 +2,7 @@ error[E0560]: union `U` has no field named `principle` --> $DIR/union-suggest-field.rs:20:17 | 20 | let u = U { principle: 0 }; - | ^^^^^^^^^^ field does not exist - did you mean `principal`? + | ^^^^^^^^^ field does not exist - did you mean `principal`? error[E0609]: no field `principial` on type `U` --> $DIR/union-suggest-field.rs:22:15 From 6818551c6f3a434752d0322fce03d594513b5ebd Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 18 Feb 2018 13:33:56 -0800 Subject: [PATCH 38/42] bump pulldown --- src/Cargo.lock | 8 ++++---- src/librustdoc/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Cargo.lock b/src/Cargo.lock index d8306c66daf84..7dc8374e3e8fd 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1086,7 +1086,7 @@ dependencies = [ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pulldown-cmark 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1383,7 +1383,7 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2104,7 +2104,7 @@ dependencies = [ name = "rustdoc" version = "0.0.0" dependencies = [ - "pulldown-cmark 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2886,7 +2886,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6dda33d67c26f0aac90d324ab2eb7239c819fc7b2552fe9faa4fe88441edc8" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" "checksum pulldown-cmark 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "378e941dbd392c101f2cb88097fa4d7167bc421d4b88de3ff7dbee503bc3233b" -"checksum pulldown-cmark 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a656fdb8b6848f896df5e478a0eb9083681663e37dcb77dd16981ff65329fe8b" +"checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" "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" diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 09d0a0f610b7b..88b0f4486223c 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -10,5 +10,5 @@ path = "lib.rs" doctest = false [dependencies] -pulldown-cmark = { version = "0.1.0", default-features = false } +pulldown-cmark = { version = "0.1.2", default-features = false } tempdir = "0.3" From f60aeec5d564cf3ca0628db55abb8c62276e102f Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 18 Feb 2018 13:37:52 -0800 Subject: [PATCH 39/42] Include shortcut links in markdown_links --- src/librustdoc/html/markdown.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index fedd802ce557f..bc7d1066ba45e 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -731,18 +731,30 @@ pub fn markdown_links(md: &str) -> Vec { opts.insert(OPTION_ENABLE_TABLES); opts.insert(OPTION_ENABLE_FOOTNOTES); - let p = Parser::new_ext(md, opts); - - let iter = Footnotes::new(HeadingLinks::new(p, None)); let mut links = vec![]; + let shortcut_links = RefCell::new(vec![]); + + { + let push = |_: &str, s: &str| { + shortcut_links.borrow_mut().push(s.to_owned()); + None + }; + let p = Parser::new_with_broken_link_callback(md, opts, + Some(&push)); - for ev in iter { - if let Event::Start(Tag::Link(dest, _)) = ev { - debug!("found link: {}", dest); - links.push(dest.into_owned()); + let iter = Footnotes::new(HeadingLinks::new(p, None)); + + for ev in iter { + if let Event::Start(Tag::Link(dest, _)) = ev { + debug!("found link: {}", dest); + links.push(dest.into_owned()); + } } } + let mut shortcut_links = shortcut_links.into_inner(); + links.extend(shortcut_links.drain(..)); + links } From 1d0ae9f174dd7fbefc268dc52fcac35627afa42d Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 18 Feb 2018 14:32:34 -0800 Subject: [PATCH 40/42] Generate shortcut links --- src/librustdoc/html/markdown.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index bc7d1066ba45e..66fe2280aef7e 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -591,7 +591,15 @@ impl<'a> fmt::Display for Markdown<'a> { opts.insert(OPTION_ENABLE_TABLES); opts.insert(OPTION_ENABLE_FOOTNOTES); - let p = Parser::new_ext(md, opts); + let replacer = |_: &str, s: &str| { + if let Some(&(_, ref replace)) = links.into_iter().find(|link| &*link.0 == s) { + Some((replace.clone(), s.to_owned())) + } else { + None + } + }; + + let p = Parser::new_with_broken_link_callback(md, opts, Some(&replacer)); let mut s = String::with_capacity(md.len() * 3 / 2); @@ -662,7 +670,16 @@ impl<'a> fmt::Display for MarkdownSummaryLine<'a> { // This is actually common enough to special-case if md.is_empty() { return Ok(()) } - let p = Parser::new(md); + let replacer = |_: &str, s: &str| { + if let Some(&(_, ref replace)) = links.into_iter().find(|link| &*link.0 == s) { + Some((replace.clone(), s.to_owned())) + } else { + None + } + }; + + let p = Parser::new_with_broken_link_callback(md, Options::empty(), + Some(&replacer)); let mut s = String::new(); From a04c124078ae9142588363e54877b9a151fb2043 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 18 Feb 2018 14:55:48 -0800 Subject: [PATCH 41/42] Add test --- src/test/rustdoc/intra-links.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/rustdoc/intra-links.rs b/src/test/rustdoc/intra-links.rs index 4726323e11cef..c822d0f8b21b8 100644 --- a/src/test/rustdoc/intra-links.rs +++ b/src/test/rustdoc/intra-links.rs @@ -77,3 +77,15 @@ pub trait SoAmbiguous {} #[allow(bad_style)] pub fn SoAmbiguous() {} + + +// @has - '//a/@href' '../intra_links/struct.ThisType.html' +// @has - '//a/@href' '../intra_links/struct.ThisType.html#method.this_method' +// @has - '//a/@href' '../intra_links/enum.ThisEnum.html' +// @has - '//a/@href' '../intra_links/enum.ThisEnum.html#ThisVariant.v' +/// Shortcut links for: +/// * [`ThisType`] +/// * [`ThisType::this_method`] +/// * [ThisEnum] +/// * [ThisEnum::ThisVariant] +pub struct SomeOtherType; From 5fdc10c68b3b5a42b17555be18baf5d7d60d55e6 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 18 Feb 2018 15:44:24 -0800 Subject: [PATCH 42/42] Filter out non-macros in resolve_macro Fixes https://github.com/rust-lang/rust/issues/48341 --- src/librustdoc/clean/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7f51b8f68ae49..6accda1d85179 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1008,7 +1008,7 @@ fn resolve(cx: &DocContext, path_str: &str, is_val: bool) -> Result<(Def, Option /// Resolve a string as a macro fn macro_resolve(cx: &DocContext, path_str: &str) -> Option { - use syntax::ext::base::MacroKind; + use syntax::ext::base::{MacroKind, SyntaxExtension}; use syntax::ext::hygiene::Mark; let segment = ast::PathSegment { identifier: ast::Ident::from_str(path_str), @@ -1025,7 +1025,11 @@ fn macro_resolve(cx: &DocContext, path_str: &str) -> Option { let res = resolver .resolve_macro_to_def_inner(mark, &path, MacroKind::Bang, false); if let Ok(def) = res { - Some(def) + if let SyntaxExtension::DeclMacro(..) = *resolver.get_macro(def) { + Some(def) + } else { + None + } } else if let Some(def) = resolver.all_macros.get(&path_str.into()) { Some(*def) } else {