diff --git a/.gitignore b/.gitignore index bf66eabc1c80..8b141416e4f6 100644 --- a/.gitignore +++ b/.gitignore @@ -73,12 +73,13 @@ __pycache__/ /obj/ /rt/ /rustllvm/ -/src/libunicode/DerivedCoreProperties.txt -/src/libunicode/EastAsianWidth.txt -/src/libunicode/HangulSyllableType.txt -/src/libunicode/PropList.txt -/src/libunicode/Scripts.txt -/src/libunicode/UnicodeData.txt +/src/libstd_unicode/DerivedCoreProperties.txt +/src/libstd_unicode/DerivedNormalizationProps.txt +/src/libstd_unicode/PropList.txt +/src/libstd_unicode/ReadMe.txt +/src/libstd_unicode/Scripts.txt +/src/libstd_unicode/SpecialCasing.txt +/src/libstd_unicode/UnicodeData.txt /stage[0-9]+/ /target /test/ diff --git a/.mailmap b/.mailmap index e0759cb85638..c5a9fe246a01 100644 --- a/.mailmap +++ b/.mailmap @@ -152,7 +152,7 @@ NAKASHIMA, Makoto Nathan Wilson Nathaniel Herman Nathaniel Herman Neil Pankey -Nicholas Mazzuca Nicholas +Nicole Mazzuca Nicole Nick Platt Nif Ward Oliver Schneider oli-obk diff --git a/.travis.yml b/.travis.yml index 26cabf92bdac..6cb2b8021472 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,19 +13,20 @@ osx_image: xcode8.2 matrix: include: # Linux builders, all docker images - - env: IMAGE=arm-android - - env: IMAGE=cross - - env: IMAGE=i686-gnu + - env: IMAGE=arm-android DEPLOY=1 + - env: IMAGE=cross DEPLOY=1 + - env: IMAGE=dist-arm-unknown-linux-gnueabi DEPLOY=1 + - env: IMAGE=dist-x86_64-unknown-freebsd DEPLOY=1 + - env: IMAGE=i686-gnu DEPLOY=1 - env: IMAGE=i686-gnu-nopt - - env: IMAGE=x86_64-freebsd - - env: IMAGE=x86_64-gnu + - env: IMAGE=x86_64-gnu DEPLOY=1 - env: IMAGE=x86_64-gnu-full-bootstrap - env: IMAGE=x86_64-gnu-aux - env: IMAGE=x86_64-gnu-debug - env: IMAGE=x86_64-gnu-nopt - env: IMAGE=x86_64-gnu-make - env: IMAGE=x86_64-gnu-llvm-3.7 ALLOW_PR=1 RUST_BACKTRACE=1 - - env: IMAGE=x86_64-musl + - env: IMAGE=x86_64-musl DEPLOY=1 - env: IMAGE=x86_64-gnu-distcheck # OSX builders @@ -34,27 +35,44 @@ matrix: RUST_CONFIGURE_ARGS=--build=x86_64-apple-darwin SRC=. os: osx + before_script: &osx_before_script > + ulimit -c unlimited install: &osx_install_sccache > curl -L https://api.pub.build.mozilla.org/tooltool/sha512/d0025b286468cc5ada83b23d3fafbc936b9f190eaa7d4a981715b18e8e3bf720a7bcee7bfe758cfdeb8268857f6098fd52dcdd8818232692a30ce91039936596 | tar xJf - -C /usr/local/bin --strip-components=1 + after_failure: &osx_after_failure > + echo 'bt all' > cmds; + for file in $(ls /cores); do + echo core file $file; + lldb -c $file `which ld` -b -s cmds; + done + - env: > - RUST_CHECK_TARGET=check + SCRIPT="./x.py test && ./x.py dist" RUST_CONFIGURE_ARGS=--build=i686-apple-darwin SRC=. + DEPLOY=1 os: osx + before_script: *osx_before_script install: *osx_install_sccache + after_failure: *osx_after_failure - env: > RUST_CHECK_TARGET=check RUST_CONFIGURE_ARGS=--build=x86_64-apple-darwin --disable-rustbuild SRC=. os: osx + before_script: *osx_before_script install: *osx_install_sccache + after_failure: *osx_after_failure - env: > - RUST_CHECK_TARGET= + RUST_CHECK_TARGET=dist RUST_CONFIGURE_ARGS=--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios SRC=. + DEPLOY=1 os: osx + before_script: *osx_before_script install: *osx_install_sccache + after_failure: *osx_after_failure env: global: @@ -68,10 +86,10 @@ script: if [ "$ALLOW_PR" = "" ] && [ "$TRAVIS_BRANCH" != "auto" ]; then echo skipping, not a full build; elif [ "$TRAVIS_OS_NAME" = "osx" ]; then - git submodule update --init && + travis_retry git submodule update --init && src/ci/run.sh; else - git submodule update --init && + travis_retry git submodule update --init && src/ci/docker/run.sh $IMAGE; fi @@ -90,3 +108,29 @@ notifications: cache: directories: - $HOME/docker + +before_deploy: + - mkdir -p deploy/$TRAVIS_COMMIT + - > + if [ "$TRAVIS_OS_NAME" == "osx" ]; then + cp build/dist/*.tar.gz deploy/$TRAVIS_COMMIT && + find "deploy/$TRAVIS_COMMIT" -maxdepth 1 -type f -exec sh -c 'shasum -a 256 -b "{}" > "{}.sha256"' \;; + else + cp obj/build/dist/*.tar.gz deploy/$TRAVIS_COMMIT && + find "deploy/$TRAVIS_COMMIT" -maxdepth 1 -type f -exec sh -c 'sha256sum -b "{}" > "{}.sha256"' \;; + fi + +deploy: + - provider: s3 + bucket: rust-lang-ci + skip_cleanup: true + local_dir: deploy + upload_dir: rustc-builds + acl: public_read + region: us-east-1 + access_key_id: AKIAIPQVNYF2T3DTYIWQ + secret_access_key: + secure: "FBqDqOTeIPMu6v/WYPf4CFSlh9rLRZGKVtpLa5KkyuOhXRTrnEzBduEtS8/FMIxdQImvurhSvxWvqRybMOi4qoVfjMqqpHAI7uBbidbrvAcJoHNsx6BgUNVCIoH6a0UsAjTUtm6/YPIpzbHoLZXPL0GrHPMk6Mu04qVSmcYNWn4=" + on: + branch: auto + condition: $DEPLOY = 1 diff --git a/COMPILER_TESTS.md b/COMPILER_TESTS.md index 91975c1f9ed9..58df1aae6d3f 100644 --- a/COMPILER_TESTS.md +++ b/COMPILER_TESTS.md @@ -45,6 +45,10 @@ whole, instead of just a few lines inside the test. * `should-fail` indicates that the test should fail; used for "meta testing", where we test the compiletest program itself to check that it will generate errors in appropriate scenarios. This header is ignored for pretty-printer tests. +* `gate-test-X` where `X` is a feature marks the test as "gate test" for feature X. + Such tests are supposed to ensure that the compiler errors when usage of a gated + feature is attempted without the proper `#![feature(X)]` tag. + Each unstable lang feature is required to have a gate test. ## Revisions diff --git a/appveyor.yml b/appveyor.yml index a4b5cbc11525..346ef0d8faad 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,10 +8,12 @@ environment: # 32/64 bit MSVC - MSYS_BITS: 64 RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc - RUST_CHECK_TARGET: check + SCRIPT: python x.py test && python x.py dist + DEPLOY: 1 - MSYS_BITS: 32 RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc - RUST_CHECK_TARGET: check + SCRIPT: python x.py test && python x.py dist + DEPLOY: 1 # MSVC makefiles - MSYS_BITS: 64 @@ -50,10 +52,11 @@ environment: # too long on appveyor and this is tested by rustbuild below. - MSYS_BITS: 32 RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu - RUST_CHECK_TARGET: check + SCRIPT: python x.py test && python x.py dist MINGW_URL: https://s3.amazonaws.com/rust-lang-ci MINGW_ARCHIVE: i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z MINGW_DIR: mingw32 + DEPLOY: 1 - MSYS_BITS: 32 RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --disable-rustbuild @@ -63,11 +66,12 @@ environment: MINGW_DIR: mingw32 - MSYS_BITS: 64 - RUST_CHECK_TARGET: check + SCRIPT: python x.py test && python x.py dist RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu MINGW_URL: https://s3.amazonaws.com/rust-lang-ci MINGW_ARCHIVE: x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z MINGW_DIR: mingw64 + DEPLOY: 1 clone_depth: 1 build: false @@ -123,6 +127,32 @@ branches: only: - auto +before_deploy: + - ps: | + New-Item -Path deploy -ItemType directory + Get-ChildItem -Path build\dist -Filter '*.tar.gz' | Move-Item -Destination deploy + Get-FileHash .\deploy\* | ForEach-Object { + [io.file]::WriteAllText($_.Path + ".sha256", $_.Hash.ToLower() + "`n") + } + Get-ChildItem -Path deploy | Foreach-Object { + Push-AppveyorArtifact $_.FullName -FileName ${env:APPVEYOR_REPO_COMMIT}/$_ + } + +deploy: + - provider: S3 + skip_cleanup: true + access_key_id: AKIAIPQVNYF2T3DTYIWQ + secret_access_key: + secure: +11jsUNFTQ9dq5Ad1i2+PeUJaXluFJ0zIJAXESE1dFT3Kdjku4/eDdgyjgsB6GnV + bucket: rust-lang-ci + set_public: true + region: us-east-1 + artifact: /.*\.(tar.gz|sha256)/ + folder: rustc-builds + on: + branch: auto + DEPLOY: 1 + # init: # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) # on_finish: diff --git a/configure b/configure index ee5922b1f142..fa81684fb48e 100755 --- a/configure +++ b/configure @@ -541,6 +541,18 @@ case $CFG_CPUTYPE in CFG_CPUTYPE=x86_64 ;; + mips | mips64) + if [ "$CFG_CPUTYPE" = "mips64" ]; then + CFG_OSTYPE="${CFG_OSTYPE}abi64" + fi + ENDIAN=$(printf '\1' | od -dAn) + if [ "$ENDIAN" -eq 1 ]; then + CFG_CPUTYPE="${CFG_CPUTYPE}el" + elif [ "$ENDIAN" -ne 256 ]; then + err "unknown endianness: $ENDIAN (expecting 1 for little or 256 for big)" + fi + ;; + BePC) CFG_CPUTYPE=i686 ;; @@ -647,6 +659,7 @@ opt_nosave debug-assertions 0 "build with debugging assertions" opt_nosave llvm-release-debuginfo 0 "build LLVM with debugger metadata" opt_nosave debuginfo 0 "build with debugger metadata" opt_nosave debuginfo-lines 0 "build with line number debugger metadata" +opt_nosave debuginfo-only-std 0 "build only libstd with debugging information" opt_nosave debug-jemalloc 0 "build jemalloc with --enable-debug --enable-fill" valopt localstatedir "/var/lib" "local state directory" @@ -733,15 +746,17 @@ case "$CFG_RELEASE_CHANNEL" in nightly ) msg "overriding settings for $CFG_RELEASE_CHANNEL" CFG_ENABLE_LLVM_ASSERTIONS=1 - - # FIXME(#37364) shouldn't have to disable this on windows-gnu + # FIXME(stage0) re-enable this on the next stage0 now that #35566 is + # fixed case "$CFG_BUILD" in *-pc-windows-gnu) ;; *) - CFG_ENABLE_DEBUGINFO_LINES=1 + CFG_ENABLE_DEBUGINFO_LINES=1 + CFG_ENABLE_DEBUGINFO_ONLY_STD=1 ;; esac + ;; beta | stable) msg "overriding settings for $CFG_RELEASE_CHANNEL" @@ -749,7 +764,8 @@ case "$CFG_RELEASE_CHANNEL" in *-pc-windows-gnu) ;; *) - CFG_ENABLE_DEBUGINFO_LINES=1 + CFG_ENABLE_DEBUGINFO_LINES=1 + CFG_ENABLE_DEBUGINFO_ONLY_STD=1 ;; esac ;; @@ -785,6 +801,7 @@ if [ -n "$CFG_ENABLE_DEBUG_ASSERTIONS" ]; then putvar CFG_ENABLE_DEBUG_ASSERTION if [ -n "$CFG_ENABLE_LLVM_RELEASE_DEBUGINFO" ]; then putvar CFG_ENABLE_LLVM_RELEASE_DEBUGINFO; fi if [ -n "$CFG_ENABLE_DEBUGINFO" ]; then putvar CFG_ENABLE_DEBUGINFO; fi if [ -n "$CFG_ENABLE_DEBUGINFO_LINES" ]; then putvar CFG_ENABLE_DEBUGINFO_LINES; fi +if [ -n "$CFG_ENABLE_DEBUGINFO_ONLY_STD" ]; then putvar CFG_ENABLE_DEBUGINFO_ONLY_STD; fi if [ -n "$CFG_ENABLE_DEBUG_JEMALLOC" ]; then putvar CFG_ENABLE_DEBUG_JEMALLOC; fi step_msg "looking for build programs" diff --git a/mk/main.mk b/mk/main.mk index 2c646450d018..9cd30be6531d 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -175,7 +175,7 @@ endif # that the snapshot will be generated with a statically linked rustc so we only # have to worry about the distribution of one file (with its native dynamic # dependencies) -RUSTFLAGS_STAGE0 += -C prefer-dynamic -C no-stack-check +RUSTFLAGS_STAGE0 += -C prefer-dynamic RUSTFLAGS_STAGE1 += -C prefer-dynamic RUST_LIB_FLAGS_ST2 += -C prefer-dynamic RUST_LIB_FLAGS_ST3 += -C prefer-dynamic diff --git a/src/Cargo.lock b/src/Cargo.lock index 3da29933c812..d153945dc091 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -569,7 +569,6 @@ dependencies = [ "rustc 0.0.0", "rustc_back 0.0.0", "rustc_const_eval 0.0.0", - "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", "rustc_driver 0.0.0", "rustc_errors 0.0.0", diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 35f8fb43f7b5..1eda1608c470 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -6,18 +6,22 @@ version = "0.0.0" [lib] name = "bootstrap" path = "lib.rs" +doctest = false [[bin]] name = "bootstrap" path = "bin/main.rs" +test = false [[bin]] name = "rustc" path = "bin/rustc.rs" +test = false [[bin]] name = "rustdoc" path = "bin/rustdoc.rs" +test = false [dependencies] build_helper = { path = "../build_helper" } diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 89d297760e28..85e8dbce1a95 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -72,7 +72,7 @@ def download(path, url, probably_big, verbose): option = "-#" else: option = "-s" - run(["curl", option, "-Sf", "-o", path, url], verbose=verbose) + run(["curl", option, "--retry", "3", "-Sf", "-o", path, url], verbose=verbose) def verify(path, sha_path, verbose): diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 6db1afa54a62..cafdd3aec65b 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -457,6 +457,8 @@ fn krate_android(build: &Build, let output = output(Command::new("adb").arg("shell").arg(&program)); println!("{}", output); + + t!(fs::create_dir_all(build.out.join("tmp"))); build.run(Command::new("adb") .arg("pull") .arg(&log) @@ -516,6 +518,7 @@ pub fn android_copy_libs(build: &Build, } println!("Android copy libs to emulator ({})", target); + build.run(Command::new("adb").arg("wait-for-device")); build.run(Command::new("adb").arg("remount")); build.run(Command::new("adb").args(&["shell", "rm", "-r", ADB_TEST_DIR])); build.run(Command::new("adb").args(&["shell", "mkdir", ADB_TEST_DIR])); @@ -568,3 +571,14 @@ pub fn distcheck(build: &Build) { .arg("check") .current_dir(&dir)); } + +/// Test the build system itself +pub fn bootstrap(build: &Build) { + let mut cmd = Command::new(&build.cargo); + cmd.arg("test") + .current_dir(build.src.join("src/bootstrap")) + .env("CARGO_TARGET_DIR", build.out.join("bootstrap")) + .env("RUSTC", &build.rustc); + cmd.arg("--").args(&build.flags.cmd.test_args()); + build.run(&mut cmd); +} diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index a7633998aad8..0eeb799672cf 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -16,7 +16,6 @@ //! compiler. This module is also responsible for assembling the sysroot as it //! goes along from the output of the previous stage. -use std::cmp; use std::collections::HashMap; use std::fs::{self, File}; use std::path::{Path, PathBuf}; @@ -59,7 +58,7 @@ pub fn std(build: &Build, target: &str, compiler: &Compiler) { } build.run(&mut cargo); - update_mtime(&libstd_stamp(build, &compiler, target)); + update_mtime(build, &libstd_stamp(build, &compiler, target)); } /// Link all libstd rlibs/dylibs into the sysroot location. @@ -145,7 +144,7 @@ pub fn test(build: &Build, target: &str, compiler: &Compiler) { cargo.arg("--manifest-path") .arg(build.src.join("src/rustc/test_shim/Cargo.toml")); build.run(&mut cargo); - update_mtime(&libtest_stamp(build, compiler, target)); + update_mtime(build, &libtest_stamp(build, compiler, target)); } /// Same as `std_link`, only for libtest @@ -186,9 +185,16 @@ pub fn rustc(build: &Build, target: &str, compiler: &Compiler) { cargo.env("CFG_RELEASE", &build.release) .env("CFG_RELEASE_CHANNEL", &build.config.channel) .env("CFG_VERSION", &build.version) - .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or(String::new())) + .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or(PathBuf::new())) .env("CFG_LIBDIR_RELATIVE", "lib"); + // If we're not building a compiler with debugging information then remove + // these two env vars which would be set otherwise. + if build.config.rust_debuginfo_only_std { + cargo.env_remove("RUSTC_DEBUGINFO"); + cargo.env_remove("RUSTC_DEBUGINFO_LINES"); + } + if let Some(ref ver_date) = build.ver_date { cargo.env("CFG_VER_DATE", ver_date); } @@ -258,11 +264,6 @@ fn compiler_file(compiler: &Path, file: &str) -> PathBuf { } pub fn create_sysroot(build: &Build, compiler: &Compiler) { - // nothing to do in stage0 - if compiler.stage == 0 { - return - } - let sysroot = build.sysroot(compiler); let _ = fs::remove_dir_all(&sysroot); t!(fs::create_dir_all(&sysroot)); @@ -388,26 +389,39 @@ pub fn tool(build: &Build, stage: u32, host: &str, tool: &str) { } /// Updates the mtime of a stamp file if necessary, only changing it if it's -/// older than some other file in the same directory. +/// older than some other library file in the same directory. /// /// We don't know what file Cargo is going to output (because there's a hash in /// the file name) but we know where it's going to put it. We use this helper to /// detect changes to that output file by looking at the modification time for /// all files in a directory and updating the stamp if any are newer. -fn update_mtime(path: &Path) { - let mut max = None; - if let Ok(entries) = path.parent().unwrap().read_dir() { - for entry in entries.map(|e| t!(e)) { - if t!(entry.file_type()).is_file() { - let meta = t!(entry.metadata()); - let time = FileTime::from_last_modification_time(&meta); - max = cmp::max(max, Some(time)); - } - } - } +/// +/// Note that we only consider Rust libraries as that's what we're interested in +/// propagating changes from. Files like executables are tracked elsewhere. +fn update_mtime(build: &Build, path: &Path) { + let entries = match path.parent().unwrap().join("deps").read_dir() { + Ok(entries) => entries, + Err(_) => return, + }; + let files = entries.map(|e| t!(e)).filter(|e| t!(e.file_type()).is_file()); + let files = files.filter(|e| { + let filename = e.file_name(); + let filename = filename.to_str().unwrap(); + filename.ends_with(".rlib") || + filename.ends_with(".lib") || + is_dylib(&filename) + }); + let max = files.max_by_key(|entry| { + let meta = t!(entry.metadata()); + FileTime::from_last_modification_time(&meta) + }); + let max = match max { + Some(max) => max, + None => return, + }; - if !max.is_none() && max <= Some(mtime(path)) { - return + if mtime(&max.path()) > mtime(path) { + build.verbose(&format!("updating {:?} as {:?} changed", path, max.path())); + t!(File::create(path)); } - t!(File::create(path)); } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 11c5b374ed96..8e2129758f6d 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -63,6 +63,7 @@ pub struct Config { pub rust_debug_assertions: bool, pub rust_debuginfo: bool, pub rust_debuginfo_lines: bool, + pub rust_debuginfo_only_std: bool, pub rust_rpath: bool, pub rustc_default_linker: Option, pub rustc_default_ar: Option, @@ -86,10 +87,10 @@ pub struct Config { pub quiet_tests: bool, // Fallback musl-root for all targets pub musl_root: Option, - pub prefix: Option, - pub docdir: Option, - pub libdir: Option, - pub mandir: Option, + pub prefix: Option, + pub docdir: Option, + pub libdir: Option, + pub mandir: Option, pub codegen_tests: bool, pub nodejs: Option, pub gdb: Option, @@ -144,6 +145,9 @@ struct Build { #[derive(RustcDecodable, Default, Clone)] struct Install { prefix: Option, + mandir: Option, + docdir: Option, + libdir: Option, } /// TOML representation of how the LLVM build is configured. @@ -179,6 +183,7 @@ struct Rust { debug_assertions: Option, debuginfo: Option, debuginfo_lines: Option, + debuginfo_only_std: Option, debug_jemalloc: Option, use_jemalloc: Option, backtrace: Option, @@ -272,7 +277,10 @@ impl Config { set(&mut config.full_bootstrap, build.full_bootstrap); if let Some(ref install) = toml.install { - config.prefix = install.prefix.clone(); + config.prefix = install.prefix.clone().map(PathBuf::from); + config.mandir = install.mandir.clone().map(PathBuf::from); + config.docdir = install.docdir.clone().map(PathBuf::from); + config.libdir = install.libdir.clone().map(PathBuf::from); } if let Some(ref llvm) = toml.llvm { @@ -298,6 +306,7 @@ impl Config { set(&mut config.rust_debug_assertions, rust.debug_assertions); set(&mut config.rust_debuginfo, rust.debuginfo); set(&mut config.rust_debuginfo_lines, rust.debuginfo_lines); + set(&mut config.rust_debuginfo_only_std, rust.debuginfo_only_std); set(&mut config.rust_optimize, rust.optimize); set(&mut config.rust_optimize_tests, rust.optimize_tests); set(&mut config.rust_debuginfo_tests, rust.debuginfo_tests); @@ -390,6 +399,7 @@ impl Config { ("DEBUG_ASSERTIONS", self.rust_debug_assertions), ("DEBUGINFO", self.rust_debuginfo), ("DEBUGINFO_LINES", self.rust_debuginfo_lines), + ("DEBUGINFO_ONLY_STD", self.rust_debuginfo_only_std), ("JEMALLOC", self.use_jemalloc), ("DEBUG_JEMALLOC", self.debug_jemalloc), ("RPATH", self.rust_rpath), @@ -459,16 +469,16 @@ impl Config { self.channel = value.to_string(); } "CFG_PREFIX" => { - self.prefix = Some(value.to_string()); + self.prefix = Some(PathBuf::from(value)); } "CFG_DOCDIR" => { - self.docdir = Some(value.to_string()); + self.docdir = Some(PathBuf::from(value)); } "CFG_LIBDIR" => { - self.libdir = Some(value.to_string()); + self.libdir = Some(PathBuf::from(value)); } "CFG_MANDIR" => { - self.mandir = Some(value.to_string()); + self.mandir = Some(PathBuf::from(value)); } "CFG_LLVM_ROOT" if value.len() > 0 => { let target = self.target_config.entry(self.build.clone()) diff --git a/src/bootstrap/config.toml.example b/src/bootstrap/config.toml.example index 69210c4959b9..47e50cb79b41 100644 --- a/src/bootstrap/config.toml.example +++ b/src/bootstrap/config.toml.example @@ -124,7 +124,16 @@ [install] # Instead of installing to /usr/local, install to this path instead. -#prefix = "/path/to/install" +#prefix = "/usr/local" + +# Where to install libraries in `prefix` above +#libdir = "lib" + +# Where to install man pages in `prefix` above +#mandir = "share/man" + +# Where to install documentation in `prefix` above +#docdir = "share/doc/rust" # ============================================================================= # Options for compiling Rust code itself @@ -149,6 +158,11 @@ # Whether or not line number debug information is emitted #debuginfo-lines = false +# Whether or not to only build debuginfo for the standard library if enabled. +# If enabled, this will not compile the compiler with debuginfo, just the +# standard library. +#debuginfo-only-std = false + # Whether or not jemalloc is built and enabled #use-jemalloc = true diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 9b0c7a04d6be..dc45d3817fe3 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -354,22 +354,15 @@ pub fn analysis(build: &Build, compiler: &Compiler, target: &str) { } /// Creates the `rust-src` installer component and the plain source tarball -pub fn rust_src(build: &Build, host: &str) { +pub fn rust_src(build: &Build) { println!("Dist src"); - if host != build.config.build { - println!("\tskipping, not a build host"); - return - } - - let plain_name = format!("rustc-{}-src", package_vers(build)); let name = format!("rust-src-{}", package_vers(build)); let image = tmpdir(build).join(format!("{}-image", name)); let _ = fs::remove_dir_all(&image); let dst = image.join("lib/rustlib/src"); let dst_src = dst.join("rust"); - let plain_dst_src = dst.join(&plain_name); t!(fs::create_dir_all(&dst_src)); // This is the set of root paths which will become part of the source package @@ -449,7 +442,11 @@ pub fn rust_src(build: &Build, host: &str) { build.run(&mut cmd); // Rename directory, so that root folder of tarball has the correct name - t!(fs::rename(&dst_src, &plain_dst_src)); + let plain_name = format!("rustc-{}-src", package_vers(build)); + let plain_dst_src = tmpdir(build).join(&plain_name); + let _ = fs::remove_dir_all(&plain_dst_src); + t!(fs::create_dir_all(&plain_dst_src)); + cp_r(&dst_src, &plain_dst_src); // Create the version file write_file(&plain_dst_src.join("version"), build.version.as_bytes()); @@ -458,10 +455,11 @@ pub fn rust_src(build: &Build, host: &str) { let mut cmd = Command::new("tar"); cmd.arg("-czf").arg(sanitize_sh(&rust_src_location(build))) .arg(&plain_name) - .current_dir(&dst); + .current_dir(tmpdir(build)); build.run(&mut cmd); t!(fs::remove_dir_all(&image)); + t!(fs::remove_dir_all(&plain_dst_src)); } fn install(src: &Path, dstdir: &Path, perms: u32) { diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index bbbf5cba8a1a..d1c9918a7337 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -57,12 +57,12 @@ pub fn rustbook(build: &Build, target: &str, name: &str) { /// `STAMP` alongw ith providing the various header/footer HTML we've cutomized. /// /// In the end, this is just a glorified wrapper around rustdoc! -pub fn standalone(build: &Build, stage: u32, target: &str) { - println!("Documenting stage{} standalone ({})", stage, target); +pub fn standalone(build: &Build, target: &str) { + println!("Documenting standalone ({})", target); let out = build.doc_out(target); t!(fs::create_dir_all(&out)); - let compiler = Compiler::new(stage, &build.config.build); + let compiler = Compiler::new(0, &build.config.build); let favicon = build.src.join("src/doc/favicon.inc"); let footer = build.src.join("src/doc/footer.inc"); @@ -151,8 +151,25 @@ pub fn std(build: &Build, stage: u32, target: &str) { let mut cargo = build.cargo(&compiler, Mode::Libstd, target, "doc"); cargo.arg("--manifest-path") .arg(build.src.join("src/rustc/std_shim/Cargo.toml")) - .arg("--features").arg(build.std_features()) - .arg("-p").arg("std"); + .arg("--features").arg(build.std_features()); + + // We don't want to build docs for internal std dependencies unless + // in compiler-docs mode. When not in that mode, we whitelist the crates + // for which docs must be built. + if build.config.compiler_docs { + cargo.arg("-p").arg("std"); + } else { + cargo.arg("--no-deps"); + for krate in &["alloc", "collections", "core", "std", "std_unicode"] { + cargo.arg("-p").arg(krate); + // Create all crate output directories first to make sure rustdoc uses + // relative links. + // FIXME: Cargo should probably do this itself. + t!(fs::create_dir_all(out_dir.join(krate))); + } + } + + build.run(&mut cargo); cp_r(&out_dir, &out) } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index b2412fbb3c84..1be823417d76 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -67,6 +67,7 @@ pub enum Subcommand { }, Clean, Dist { + paths: Vec, install: bool, }, } @@ -249,6 +250,7 @@ To learn more about a subcommand, run `./x.py -h` opts.optflag("", "install", "run installer as well"); m = parse(&opts); Subcommand::Dist { + paths: remaining_as_path(&m), install: m.opt_present("install"), } } diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 9bc5a7c00aba..efc460f35838 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -13,9 +13,9 @@ //! This module is responsible for installing the standard library, //! compiler, and documentation. +use std::env; use std::fs; -use std::borrow::Cow; -use std::path::Path; +use std::path::{Path, PathBuf, Component}; use std::process::Command; use Build; @@ -23,23 +23,35 @@ use dist::{package_vers, sanitize_sh, tmpdir}; /// Installs everything. pub fn install(build: &Build, stage: u32, host: &str) { - let prefix = build.config.prefix.as_ref().clone().map(|x| Path::new(x)) - .unwrap_or(Path::new("/usr/local")); - let docdir = build.config.docdir.as_ref().clone().map(|x| Cow::Borrowed(Path::new(x))) - .unwrap_or(Cow::Owned(prefix.join("share/doc/rust"))); - let libdir = build.config.libdir.as_ref().clone().map(|x| Cow::Borrowed(Path::new(x))) - .unwrap_or(Cow::Owned(prefix.join("lib"))); - let mandir = build.config.mandir.as_ref().clone().map(|x| Cow::Borrowed(Path::new(x))) - .unwrap_or(Cow::Owned(prefix.join("share/man"))); + let prefix_default = PathBuf::from("/usr/local"); + let docdir_default = PathBuf::from("share/doc/rust"); + let mandir_default = PathBuf::from("share/man"); + let libdir_default = PathBuf::from("lib"); + let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default); + let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default); + let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default); + let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default); + + let docdir = prefix.join(docdir); + let libdir = prefix.join(libdir); + let mandir = prefix.join(mandir); + + let destdir = env::var_os("DESTDIR").map(PathBuf::from); + + let prefix = add_destdir(&prefix, &destdir); + let docdir = add_destdir(&docdir, &destdir); + let libdir = add_destdir(&libdir, &destdir); + let mandir = add_destdir(&mandir, &destdir); + let empty_dir = build.out.join("tmp/empty_dir"); t!(fs::create_dir_all(&empty_dir)); if build.config.docs { - install_sh(&build, "docs", "rust-docs", stage, host, prefix, + install_sh(&build, "docs", "rust-docs", stage, host, &prefix, &docdir, &libdir, &mandir, &empty_dir); } - install_sh(&build, "std", "rust-std", stage, host, prefix, + install_sh(&build, "std", "rust-std", stage, host, &prefix, &docdir, &libdir, &mandir, &empty_dir); - install_sh(&build, "rustc", "rustc", stage, host, prefix, + install_sh(&build, "rustc", "rustc", stage, host, &prefix, &docdir, &libdir, &mandir, &empty_dir); t!(fs::remove_dir_all(&empty_dir)); } @@ -59,3 +71,17 @@ fn install_sh(build: &Build, package: &str, name: &str, stage: u32, host: &str, .arg("--disable-ldconfig"); build.run(&mut cmd); } + +fn add_destdir(path: &Path, destdir: &Option) -> PathBuf { + let mut ret = match *destdir { + Some(ref dest) => dest.clone(), + None => return path.to_path_buf(), + }; + for part in path.components() { + match part { + Component::Normal(s) => ret.push(s), + _ => {} + } + } + return ret +} diff --git a/src/bootstrap/step.rs b/src/bootstrap/step.rs index 6a81f759dc73..65f3d241b491 100644 --- a/src/bootstrap/step.rs +++ b/src/bootstrap/step.rs @@ -316,7 +316,6 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { "codegen-units"); suite("check-incremental", "src/test/incremental", "incremental", "incremental"); - suite("check-ui", "src/test/ui", "ui", "ui"); } if build.config.build.contains("msvc") { @@ -363,8 +362,11 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { }); }; + suite("check-ui", "src/test/ui", "ui", "ui"); suite("check-rpass-full", "src/test/run-pass-fulldeps", "run-pass", "run-pass-fulldeps"); + suite("check-rfail-full", "src/test/run-fail-fulldeps", + "run-fail", "run-fail-fulldeps"); suite("check-cfail-full", "src/test/compile-fail-fulldeps", "compile-fail", "compile-fail-fulldeps"); suite("check-rmake", "src/test/run-make", "run-make", "run-make"); @@ -459,6 +461,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { .dep(|s| s.name("tool-tidy").stage(0)) .default(true) .host(true) + .only_build(true) .run(move |s| check::tidy(build, s.target)); rules.test("check-error-index", "src/tools/error_index_generator") .dep(|s| s.name("libstd")) @@ -482,6 +485,12 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { .dep(|s| s.name("libtest")) .run(move |s| check::android_copy_libs(build, &s.compiler(), s.target)); + rules.test("check-bootstrap", "src/bootstrap") + .default(true) + .host(true) + .only_build(true) + .run(move |_| check::bootstrap(build)); + // ======================================================================== // Build tools // @@ -508,17 +517,32 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { // ======================================================================== // Documentation targets rules.doc("doc-book", "src/doc/book") - .dep(move |s| s.name("tool-rustbook").target(&build.config.build).stage(0)) + .dep(move |s| { + s.name("tool-rustbook") + .host(&build.config.build) + .target(&build.config.build) + .stage(0) + }) .default(build.config.docs) .run(move |s| doc::rustbook(build, s.target, "book")); rules.doc("doc-nomicon", "src/doc/nomicon") - .dep(move |s| s.name("tool-rustbook").target(&build.config.build).stage(0)) + .dep(move |s| { + s.name("tool-rustbook") + .host(&build.config.build) + .target(&build.config.build) + .stage(0) + }) .default(build.config.docs) .run(move |s| doc::rustbook(build, s.target, "nomicon")); rules.doc("doc-standalone", "src/doc") - .dep(move |s| s.name("rustc").host(&build.config.build).target(&build.config.build)) + .dep(move |s| { + s.name("rustc") + .host(&build.config.build) + .target(&build.config.build) + .stage(0) + }) .default(build.config.docs) - .run(move |s| doc::standalone(build, s.stage, s.target)); + .run(move |s| doc::standalone(build, s.target)); rules.doc("doc-error-index", "src/tools/error_index_generator") .dep(move |s| s.name("tool-error-index").target(&build.config.build).stage(0)) .dep(move |s| s.name("librustc-link").stage(0)) @@ -550,6 +574,7 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.dist("dist-rustc", "src/librustc") .dep(move |s| s.name("rustc").host(&build.config.build)) .host(true) + .only_host_build(true) .default(true) .run(move |s| dist::rustc(build, s.stage, s.target)); rules.dist("dist-std", "src/libstd") @@ -564,9 +589,11 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { } }) .default(true) + .only_host_build(true) .run(move |s| dist::std(build, &s.compiler(), s.target)); rules.dist("dist-mingw", "path/to/nowhere") .default(true) + .only_host_build(true) .run(move |s| { if s.target.contains("pc-windows-gnu") { dist::mingw(build, s.target) @@ -575,16 +602,20 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules { rules.dist("dist-src", "src") .default(true) .host(true) - .run(move |s| dist::rust_src(build, s.target)); + .only_build(true) + .only_host_build(true) + .run(move |_| dist::rust_src(build)); rules.dist("dist-docs", "src/doc") .default(true) + .only_host_build(true) .dep(|s| s.name("default:doc")) .run(move |s| dist::docs(build, s.stage, s.target)); rules.dist("dist-analysis", "analysis") .dep(|s| s.name("dist-std")) .default(true) + .only_host_build(true) .run(move |s| dist::analysis(build, &s.compiler(), s.target)); - rules.dist("install", "src") + rules.dist("install", "path/to/nowhere") .dep(|s| s.name("default:dist")) .run(move |s| install::install(build, s.stage, s.target)); @@ -671,6 +702,14 @@ struct Rule<'a> { /// only intended for compiler hosts and not for targets that are being /// generated. host: bool, + + /// Whether this rule is only for steps where the host is the build triple, + /// not anything in hosts or targets. + only_host_build: bool, + + /// Whether this rule is only for the build triple, not anything in hosts or + /// targets. + only_build: bool, } #[derive(PartialEq)] @@ -692,6 +731,8 @@ impl<'a> Rule<'a> { kind: kind, default: false, host: false, + only_host_build: false, + only_build: false, } } } @@ -727,6 +768,16 @@ impl<'a, 'b> RuleBuilder<'a, 'b> { self.rule.host = host; self } + + fn only_build(&mut self, only_build: bool) -> &mut Self { + self.rule.only_build = only_build; + self + } + + fn only_host_build(&mut self, only_host_build: bool) -> &mut Self { + self.rule.only_host_build = only_host_build; + self + } } impl<'a, 'b> Drop for RuleBuilder<'a, 'b> { @@ -881,11 +932,11 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? Subcommand::Doc { ref paths } => (Kind::Doc, &paths[..]), Subcommand::Test { ref paths, test_args: _ } => (Kind::Test, &paths[..]), Subcommand::Bench { ref paths, test_args: _ } => (Kind::Bench, &paths[..]), - Subcommand::Dist { install } => { + Subcommand::Dist { ref paths, install } => { if install { return vec![self.sbuild.name("install")] } else { - (Kind::Dist, &[][..]) + (Kind::Dist, &paths[..]) } } Subcommand::Clean => panic!(), @@ -896,19 +947,12 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? path.ends_with(rule.path) }) }).flat_map(|rule| { - let hosts = if self.build.flags.host.len() > 0 { + let hosts = if rule.only_host_build || rule.only_build { + &self.build.config.host[..1] + } else if self.build.flags.host.len() > 0 { &self.build.flags.host } else { - if kind == Kind::Dist { - // For 'dist' steps we only distribute artifacts built from - // the build platform, so only consider that in the hosts - // array. - // NOTE: This relies on the fact that the build triple is - // always placed first, as done in `config.rs`. - &self.build.config.host[..1] - } else { - &self.build.config.host - } + &self.build.config.host }; let targets = if self.build.flags.target.len() > 0 { &self.build.flags.target @@ -928,6 +972,8 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? &self.build.flags.host[..] } else if self.build.flags.target.len() > 0 { &[] + } else if rule.only_build { + &self.build.config.host[..1] } else { &self.build.config.host[..] } @@ -955,12 +1001,7 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? // Using `steps` as the top-level targets, make a topological ordering // of what we need to do. - let mut order = Vec::new(); - let mut added = HashSet::new(); - added.insert(Step::noop()); - for step in steps.iter().cloned() { - self.fill(step, &mut order, &mut added); - } + let order = self.expand(steps); // Print out what we're doing for debugging self.build.verbose("bootstrap build plan:"); @@ -979,6 +1020,18 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? } } + /// From the top level targets `steps` generate a topological ordering of + /// all steps needed to run those steps. + fn expand(&self, steps: &[Step<'a>]) -> Vec> { + let mut order = Vec::new(); + let mut added = HashSet::new(); + added.insert(Step::noop()); + for step in steps.iter().cloned() { + self.fill(step, &mut order, &mut added); + } + return order + } + /// Performs topological sort of dependencies rooted at the `step` /// specified, pushing all results onto the `order` vector provided. /// @@ -1015,3 +1068,365 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd? order.push(step); } } + +#[cfg(test)] +mod tests { + use std::env; + + use Build; + use config::Config; + use flags::Flags; + + macro_rules! a { + ($($a:expr),*) => (vec![$($a.to_string()),*]) + } + + fn build(args: &[&str], + extra_host: &[&str], + extra_target: &[&str]) -> Build { + let mut args = args.iter().map(|s| s.to_string()).collect::>(); + args.push("--build".to_string()); + args.push("A".to_string()); + let flags = Flags::parse(&args); + + let mut config = Config::default(); + config.docs = true; + config.build = "A".to_string(); + config.host = vec![config.build.clone()]; + config.host.extend(extra_host.iter().map(|s| s.to_string())); + config.target = config.host.clone(); + config.target.extend(extra_target.iter().map(|s| s.to_string())); + + let mut build = Build::new(flags, config); + let cwd = env::current_dir().unwrap(); + build.crates.insert("std_shim".to_string(), ::Crate { + name: "std_shim".to_string(), + deps: Vec::new(), + path: cwd.join("src/std_shim"), + doc_step: "doc-std_shim".to_string(), + build_step: "build-crate-std_shim".to_string(), + test_step: "test-std_shim".to_string(), + bench_step: "bench-std_shim".to_string(), + }); + build.crates.insert("test_shim".to_string(), ::Crate { + name: "test_shim".to_string(), + deps: Vec::new(), + path: cwd.join("src/test_shim"), + doc_step: "doc-test_shim".to_string(), + build_step: "build-crate-test_shim".to_string(), + test_step: "test-test_shim".to_string(), + bench_step: "bench-test_shim".to_string(), + }); + build.crates.insert("rustc-main".to_string(), ::Crate { + name: "rustc-main".to_string(), + deps: Vec::new(), + path: cwd.join("src/rustc-main"), + doc_step: "doc-rustc-main".to_string(), + build_step: "build-crate-rustc-main".to_string(), + test_step: "test-rustc-main".to_string(), + bench_step: "bench-rustc-main".to_string(), + }); + return build + } + + #[test] + fn dist_baseline() { + let build = build(&["dist"], &[], &[]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + assert!(plan.iter().all(|s| s.host == "A" )); + assert!(plan.iter().all(|s| s.target == "A" )); + + let step = super::Step { + name: "", + stage: 2, + host: &build.config.build, + target: &build.config.build, + }; + + assert!(plan.contains(&step.name("dist-docs"))); + assert!(plan.contains(&step.name("dist-mingw"))); + assert!(plan.contains(&step.name("dist-rustc"))); + assert!(plan.contains(&step.name("dist-std"))); + assert!(plan.contains(&step.name("dist-src"))); + } + + #[test] + fn dist_with_targets() { + let build = build(&["dist"], &[], &["B"]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + assert!(plan.iter().all(|s| s.host == "A" )); + + let step = super::Step { + name: "", + stage: 2, + host: &build.config.build, + target: &build.config.build, + }; + + assert!(plan.contains(&step.name("dist-docs"))); + assert!(plan.contains(&step.name("dist-mingw"))); + assert!(plan.contains(&step.name("dist-rustc"))); + assert!(plan.contains(&step.name("dist-std"))); + assert!(plan.contains(&step.name("dist-src"))); + + assert!(plan.contains(&step.target("B").name("dist-docs"))); + assert!(plan.contains(&step.target("B").name("dist-mingw"))); + assert!(!plan.contains(&step.target("B").name("dist-rustc"))); + assert!(plan.contains(&step.target("B").name("dist-std"))); + assert!(!plan.contains(&step.target("B").name("dist-src"))); + } + + #[test] + fn dist_with_hosts() { + let build = build(&["dist"], &["B"], &[]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + + let step = super::Step { + name: "", + stage: 2, + host: &build.config.build, + target: &build.config.build, + }; + + assert!(!plan.iter().any(|s| s.host == "B")); + + assert!(plan.contains(&step.name("dist-docs"))); + assert!(plan.contains(&step.name("dist-mingw"))); + assert!(plan.contains(&step.name("dist-rustc"))); + assert!(plan.contains(&step.name("dist-std"))); + assert!(plan.contains(&step.name("dist-src"))); + + assert!(plan.contains(&step.target("B").name("dist-docs"))); + assert!(plan.contains(&step.target("B").name("dist-mingw"))); + assert!(plan.contains(&step.target("B").name("dist-rustc"))); + assert!(plan.contains(&step.target("B").name("dist-std"))); + assert!(!plan.contains(&step.target("B").name("dist-src"))); + } + + #[test] + fn dist_with_targets_and_hosts() { + let build = build(&["dist"], &["B"], &["C"]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + + let step = super::Step { + name: "", + stage: 2, + host: &build.config.build, + target: &build.config.build, + }; + + assert!(!plan.iter().any(|s| s.host == "B")); + assert!(!plan.iter().any(|s| s.host == "C")); + + assert!(plan.contains(&step.name("dist-docs"))); + assert!(plan.contains(&step.name("dist-mingw"))); + assert!(plan.contains(&step.name("dist-rustc"))); + assert!(plan.contains(&step.name("dist-std"))); + assert!(plan.contains(&step.name("dist-src"))); + + assert!(plan.contains(&step.target("B").name("dist-docs"))); + assert!(plan.contains(&step.target("B").name("dist-mingw"))); + assert!(plan.contains(&step.target("B").name("dist-rustc"))); + assert!(plan.contains(&step.target("B").name("dist-std"))); + assert!(!plan.contains(&step.target("B").name("dist-src"))); + + assert!(plan.contains(&step.target("C").name("dist-docs"))); + assert!(plan.contains(&step.target("C").name("dist-mingw"))); + assert!(!plan.contains(&step.target("C").name("dist-rustc"))); + assert!(plan.contains(&step.target("C").name("dist-std"))); + assert!(!plan.contains(&step.target("C").name("dist-src"))); + } + + #[test] + fn dist_target_with_target_flag() { + let build = build(&["dist", "--target=C"], &["B"], &["C"]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + + let step = super::Step { + name: "", + stage: 2, + host: &build.config.build, + target: &build.config.build, + }; + + assert!(!plan.iter().any(|s| s.target == "A")); + assert!(!plan.iter().any(|s| s.target == "B")); + assert!(!plan.iter().any(|s| s.host == "B")); + assert!(!plan.iter().any(|s| s.host == "C")); + + assert!(plan.contains(&step.target("C").name("dist-docs"))); + assert!(plan.contains(&step.target("C").name("dist-mingw"))); + assert!(!plan.contains(&step.target("C").name("dist-rustc"))); + assert!(plan.contains(&step.target("C").name("dist-std"))); + assert!(!plan.contains(&step.target("C").name("dist-src"))); + } + + #[test] + fn dist_host_with_target_flag() { + let build = build(&["dist", "--host=B", "--target=B"], &["B"], &["C"]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + + let step = super::Step { + name: "", + stage: 2, + host: &build.config.build, + target: &build.config.build, + }; + + assert!(!plan.iter().any(|s| s.target == "A")); + assert!(!plan.iter().any(|s| s.target == "C")); + assert!(!plan.iter().any(|s| s.host == "B")); + assert!(!plan.iter().any(|s| s.host == "C")); + + assert!(plan.contains(&step.target("B").name("dist-docs"))); + assert!(plan.contains(&step.target("B").name("dist-mingw"))); + assert!(plan.contains(&step.target("B").name("dist-rustc"))); + assert!(plan.contains(&step.target("B").name("dist-std"))); + assert!(plan.contains(&step.target("B").name("dist-src"))); + + let all = rules.expand(&plan); + println!("all rules: {:#?}", all); + assert!(!all.contains(&step.name("rustc"))); + assert!(!all.contains(&step.name("build-crate-std_shim").stage(1))); + } + + #[test] + fn build_default() { + let build = build(&["build"], &["B"], &["C"]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + + let step = super::Step { + name: "", + stage: 2, + host: &build.config.build, + target: &build.config.build, + }; + + // rustc built for all for of (A, B) x (A, B) + assert!(plan.contains(&step.name("librustc"))); + assert!(plan.contains(&step.target("B").name("librustc"))); + assert!(plan.contains(&step.host("B").target("A").name("librustc"))); + assert!(plan.contains(&step.host("B").target("B").name("librustc"))); + + // rustc never built for C + assert!(!plan.iter().any(|s| { + s.name.contains("rustc") && (s.host == "C" || s.target == "C") + })); + + // test built for everything + assert!(plan.contains(&step.name("libtest"))); + assert!(plan.contains(&step.target("B").name("libtest"))); + assert!(plan.contains(&step.host("B").target("A").name("libtest"))); + assert!(plan.contains(&step.host("B").target("B").name("libtest"))); + assert!(plan.contains(&step.host("A").target("C").name("libtest"))); + assert!(plan.contains(&step.host("B").target("C").name("libtest"))); + + let all = rules.expand(&plan); + println!("all rules: {:#?}", all); + assert!(all.contains(&step.name("rustc"))); + assert!(all.contains(&step.name("libstd"))); + } + + #[test] + fn build_filtered() { + let build = build(&["build", "--target=C"], &["B"], &["C"]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + + assert!(!plan.iter().any(|s| s.name.contains("rustc"))); + assert!(plan.iter().all(|s| { + !s.name.contains("test_shim") || s.target == "C" + })); + } + + #[test] + fn test_default() { + let build = build(&["test"], &[], &[]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + assert!(plan.iter().all(|s| s.host == "A")); + assert!(plan.iter().all(|s| s.target == "A")); + + assert!(plan.iter().any(|s| s.name.contains("-ui"))); + assert!(plan.iter().any(|s| s.name.contains("cfail"))); + assert!(plan.iter().any(|s| s.name.contains("cfail-full"))); + assert!(plan.iter().any(|s| s.name.contains("codegen-units"))); + assert!(plan.iter().any(|s| s.name.contains("debuginfo"))); + assert!(plan.iter().any(|s| s.name.contains("docs"))); + assert!(plan.iter().any(|s| s.name.contains("error-index"))); + assert!(plan.iter().any(|s| s.name.contains("incremental"))); + assert!(plan.iter().any(|s| s.name.contains("linkchecker"))); + assert!(plan.iter().any(|s| s.name.contains("mir-opt"))); + assert!(plan.iter().any(|s| s.name.contains("pfail"))); + assert!(plan.iter().any(|s| s.name.contains("rfail"))); + assert!(plan.iter().any(|s| s.name.contains("rfail-full"))); + assert!(plan.iter().any(|s| s.name.contains("rmake"))); + assert!(plan.iter().any(|s| s.name.contains("rpass"))); + assert!(plan.iter().any(|s| s.name.contains("rpass-full"))); + assert!(plan.iter().any(|s| s.name.contains("rustc-all"))); + assert!(plan.iter().any(|s| s.name.contains("rustdoc"))); + assert!(plan.iter().any(|s| s.name.contains("std-all"))); + assert!(plan.iter().any(|s| s.name.contains("test-all"))); + assert!(plan.iter().any(|s| s.name.contains("tidy"))); + assert!(plan.iter().any(|s| s.name.contains("valgrind"))); + } + + #[test] + fn test_with_a_target() { + let build = build(&["test", "--target=C"], &[], &["C"]); + let rules = super::build_rules(&build); + let plan = rules.plan(); + println!("rules: {:#?}", plan); + assert!(plan.iter().all(|s| s.stage == 2)); + assert!(plan.iter().all(|s| s.host == "A")); + assert!(plan.iter().all(|s| s.target == "C")); + + assert!(!plan.iter().any(|s| s.name.contains("-ui"))); + assert!(plan.iter().any(|s| s.name.contains("cfail"))); + assert!(!plan.iter().any(|s| s.name.contains("cfail-full"))); + assert!(plan.iter().any(|s| s.name.contains("codegen-units"))); + assert!(plan.iter().any(|s| s.name.contains("debuginfo"))); + assert!(!plan.iter().any(|s| s.name.contains("docs"))); + assert!(!plan.iter().any(|s| s.name.contains("error-index"))); + assert!(plan.iter().any(|s| s.name.contains("incremental"))); + assert!(!plan.iter().any(|s| s.name.contains("linkchecker"))); + assert!(plan.iter().any(|s| s.name.contains("mir-opt"))); + assert!(plan.iter().any(|s| s.name.contains("pfail"))); + assert!(plan.iter().any(|s| s.name.contains("rfail"))); + assert!(!plan.iter().any(|s| s.name.contains("rfail-full"))); + assert!(!plan.iter().any(|s| s.name.contains("rmake"))); + assert!(plan.iter().any(|s| s.name.contains("rpass"))); + assert!(!plan.iter().any(|s| s.name.contains("rpass-full"))); + assert!(!plan.iter().any(|s| s.name.contains("rustc-all"))); + assert!(!plan.iter().any(|s| s.name.contains("rustdoc"))); + assert!(plan.iter().any(|s| s.name.contains("std-all"))); + assert!(plan.iter().any(|s| s.name.contains("test-all"))); + assert!(!plan.iter().any(|s| s.name.contains("tidy"))); + assert!(plan.iter().any(|s| s.name.contains("valgrind"))); + } +} diff --git a/src/ci/docker/arm-android/Dockerfile b/src/ci/docker/arm-android/Dockerfile index a40e76839ecc..e2ef8070b51a 100644 --- a/src/ci/docker/arm-android/Dockerfile +++ b/src/ci/docker/arm-android/Dockerfile @@ -48,4 +48,9 @@ ENV RUST_CONFIGURE_ARGS \ --armv7-linux-androideabi-ndk=/android/ndk-arm-9 \ --i686-linux-android-ndk=/android/ndk-x86-9 \ --aarch64-linux-android-ndk=/android/ndk-aarch64 -ENV XPY_CHECK test --target arm-linux-androideabi + +# Just a smoke test in dist to see if this works for now, we should expand this +# to all the targets above eventually. +ENV SCRIPT \ + python2.7 ../x.py test --target arm-linux-androideabi && \ + python2.7 ../x.py dist --target arm-linux-androideabi diff --git a/src/ci/docker/arm-android/start-emulator.sh b/src/ci/docker/arm-android/start-emulator.sh index fcf42c1819ee..24c477d87f1a 100755 --- a/src/ci/docker/arm-android/start-emulator.sh +++ b/src/ci/docker/arm-android/start-emulator.sh @@ -13,5 +13,4 @@ set -ex ANDROID_EMULATOR_FORCE_32BIT=true \ nohup nohup emulator @arm-18 -no-window -partition-size 2047 \ 0<&- &>/dev/null & -adb wait-for-device exec "$@" diff --git a/src/ci/docker/cross/Dockerfile b/src/ci/docker/cross/Dockerfile index c5aa323f50cd..6a6cc23698be 100644 --- a/src/ci/docker/cross/Dockerfile +++ b/src/ci/docker/cross/Dockerfile @@ -56,7 +56,12 @@ ENV TARGETS=$TARGETS,wasm32-unknown-emscripten ENV RUST_CONFIGURE_ARGS \ --target=$TARGETS \ --enable-rustbuild -ENV RUST_CHECK_TARGET "" + +# Just a smoke test in dist to see if this works for now, we should expand this +# to all the targets above eventually. +ENV SCRIPT \ + python2.7 ../x.py build && \ + python2.7 ../x.py dist --target wasm32-unknown-emscripten ENV AR_s390x_unknown_linux_gnu=s390x-linux-gnu-ar \ CC_s390x_unknown_linux_gnu=s390x-linux-gnu-gcc \ diff --git a/src/ci/docker/dist-arm-unknown-linux-gnueabi/Dockerfile b/src/ci/docker/dist-arm-unknown-linux-gnueabi/Dockerfile new file mode 100644 index 000000000000..841464a42700 --- /dev/null +++ b/src/ci/docker/dist-arm-unknown-linux-gnueabi/Dockerfile @@ -0,0 +1,30 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + file \ + curl \ + ca-certificates \ + python2.7 \ + git \ + cmake \ + sudo \ + gdb \ + xz-utils \ + g++-arm-linux-gnueabi + +ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783 +RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \ + tar xJf - -C /usr/local/bin --strip-components=1 + +RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \ + dpkg -i dumb-init_*.deb && \ + rm dumb-init_*.deb +ENTRYPOINT ["/usr/bin/dumb-init", "--"] + +ENV RUST_CONFIGURE_ARGS --host=arm-unknown-linux-gnueabi +ENV SCRIPT \ + python2.7 ../x.py dist \ + --host arm-unknown-linux-gnueabi \ + --target arm-unknown-linux-gnueabi diff --git a/src/ci/docker/x86_64-freebsd/Dockerfile b/src/ci/docker/dist-x86_64-unknown-freebsd/Dockerfile similarity index 73% rename from src/ci/docker/x86_64-freebsd/Dockerfile rename to src/ci/docker/dist-x86_64-unknown-freebsd/Dockerfile index 86efa74ba3b6..44cc57c2662d 100644 --- a/src/ci/docker/x86_64-freebsd/Dockerfile +++ b/src/ci/docker/dist-x86_64-unknown-freebsd/Dockerfile @@ -28,7 +28,11 @@ RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | ENV \ AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-ar \ - CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-gcc + CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-gcc \ + CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-g++ -ENV RUST_CONFIGURE_ARGS --target=x86_64-unknown-freebsd -ENV RUST_CHECK_TARGET "" +ENV RUST_CONFIGURE_ARGS --host=x86_64-unknown-freebsd +ENV SCRIPT \ + python2.7 ../x.py dist \ + --host x86_64-unknown-freebsd \ + --target x86_64-unknown-freebsd diff --git a/src/ci/docker/x86_64-freebsd/build-toolchain.sh b/src/ci/docker/dist-x86_64-unknown-freebsd/build-toolchain.sh similarity index 98% rename from src/ci/docker/x86_64-freebsd/build-toolchain.sh rename to src/ci/docker/dist-x86_64-unknown-freebsd/build-toolchain.sh index d4bc886d50ea..0fd6beaf4c1c 100644 --- a/src/ci/docker/x86_64-freebsd/build-toolchain.sh +++ b/src/ci/docker/dist-x86_64-unknown-freebsd/build-toolchain.sh @@ -77,7 +77,7 @@ cd gcc-$GCC mkdir ../gcc-build cd ../gcc-build ../gcc-$GCC/configure \ - --enable-languages=c \ + --enable-languages=c,c++ \ --target=$ARCH-unknown-freebsd10 \ --disable-multilib \ --disable-nls \ diff --git a/src/ci/docker/i686-gnu/Dockerfile b/src/ci/docker/i686-gnu/Dockerfile index e4310232d78c..6583f09be368 100644 --- a/src/ci/docker/i686-gnu/Dockerfile +++ b/src/ci/docker/i686-gnu/Dockerfile @@ -23,4 +23,4 @@ RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-ini ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu -ENV RUST_CHECK_TARGET check +ENV SCRIPT python2.7 ../x.py test && python2.7 ../x.py dist diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 8c2c8d2a9db5..79bb17563063 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -48,7 +48,5 @@ exec docker \ --env CARGO_HOME=/cargo \ --env LOCAL_USER_ID=`id -u` \ --volume "$HOME/.cargo:/cargo" \ - --interactive \ - --tty \ rust-ci \ /checkout/src/ci/run.sh diff --git a/src/ci/docker/x86_64-gnu-debug/Dockerfile b/src/ci/docker/x86_64-gnu-debug/Dockerfile index 9ec8c6059ec3..f54e4e562f88 100644 --- a/src/ci/docker/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/x86_64-gnu-debug/Dockerfile @@ -26,4 +26,4 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --enable-debug \ --enable-optimize -ENV RUST_CHECK_TARGET "" +ENV SCRIPT python2.7 ../x.py build diff --git a/src/ci/docker/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/x86_64-gnu-distcheck/Dockerfile index 880c70866b0f..57a2c103f1e7 100644 --- a/src/ci/docker/x86_64-gnu-distcheck/Dockerfile +++ b/src/ci/docker/x86_64-gnu-distcheck/Dockerfile @@ -23,4 +23,4 @@ RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-ini ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu -ENV XPY_RUN test distcheck +ENV SCRIPT python2.7 ../x.py test distcheck diff --git a/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile b/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile index 7c079e45751c..206103b92e94 100644 --- a/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile +++ b/src/ci/docker/x86_64-gnu-full-bootstrap/Dockerfile @@ -25,4 +25,4 @@ ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --enable-full-bootstrap -ENV RUST_CHECK_TARGET "" +ENV SCRIPT python2.7 ../x.py build diff --git a/src/ci/docker/x86_64-gnu/Dockerfile b/src/ci/docker/x86_64-gnu/Dockerfile index 9d8b75c80c7f..6919487e17c3 100644 --- a/src/ci/docker/x86_64-gnu/Dockerfile +++ b/src/ci/docker/x86_64-gnu/Dockerfile @@ -23,4 +23,4 @@ RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-ini ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu -ENV RUST_CHECK_TARGET check +ENV SCRIPT python2.7 ../x.py test && python2.7 ../x.py dist diff --git a/src/ci/docker/x86_64-musl/Dockerfile b/src/ci/docker/x86_64-musl/Dockerfile index 49954157ec02..11b85a6c8fdf 100644 --- a/src/ci/docker/x86_64-musl/Dockerfile +++ b/src/ci/docker/x86_64-musl/Dockerfile @@ -29,6 +29,7 @@ RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | ENV RUST_CONFIGURE_ARGS \ --target=x86_64-unknown-linux-musl \ --musl-root-x86_64=/musl-x86_64 -ENV RUST_CHECK_TARGET check-stage2-T-x86_64-unknown-linux-musl-H-x86_64-unknown-linux-gnu ENV PATH=$PATH:/musl-x86_64/bin -ENV XPY_CHECK test --target x86_64-unknown-linux-musl +ENV SCRIPT \ + python2.7 ../x.py test --target x86_64-unknown-linux-musl && \ + python2.7 ../x.py dist --target x86_64-unknown-linux-musl diff --git a/src/ci/run.sh b/src/ci/run.sh index 3dc3cf915019..737c3622c694 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -43,14 +43,10 @@ else ncpus=$(nproc) fi -if [ ! -z "$XPY_RUN" ]; then - exec python2.7 $SRC/x.py $XPY_RUN +if [ ! -z "$SCRIPT" ]; then + sh -x -c "$SCRIPT" else make -j $ncpus tidy make -j $ncpus - if [ ! -z "$XPY_CHECK" ]; then - exec python2.7 $SRC/x.py $XPY_CHECK - else - exec make $RUST_CHECK_TARGET -j $ncpus - fi + make $RUST_CHECK_TARGET -j $ncpus fi diff --git a/src/doc/book/SUMMARY.md b/src/doc/book/SUMMARY.md index 18aa9f24580d..babbafa078f4 100644 --- a/src/doc/book/SUMMARY.md +++ b/src/doc/book/SUMMARY.md @@ -52,6 +52,7 @@ * [Borrow and AsRef](borrow-and-asref.md) * [Release Channels](release-channels.md) * [Using Rust without the standard library](using-rust-without-the-standard-library.md) + * [Procedural Macros (and custom derive)](procedural-macros.md) * [Nightly Rust](nightly-rust.md) * [Compiler Plugins](compiler-plugins.md) * [Inline Assembly](inline-assembly.md) diff --git a/src/doc/book/ffi.md b/src/doc/book/ffi.md index 41457ee67a5f..50d4d0170fc7 100644 --- a/src/doc/book/ffi.md +++ b/src/doc/book/ffi.md @@ -309,7 +309,7 @@ However it is often desired that the callback is targeted to a special Rust object. This could be the object that represents the wrapper for the respective C object. -This can be achieved by passing an raw pointer to the object down to the +This can be achieved by passing a raw pointer to the object down to the C library. The C library can then include the pointer to the Rust object in the notification. This will allow the callback to unsafely access the referenced Rust object. diff --git a/src/doc/book/lang-items.md b/src/doc/book/lang-items.md index 6a08c1b6bb46..0d6a142ca4f7 100644 --- a/src/doc/book/lang-items.md +++ b/src/doc/book/lang-items.md @@ -16,15 +16,12 @@ and one for deallocation. A freestanding program that uses the `Box` sugar for dynamic allocations via `malloc` and `free`: ```rust,ignore -#![feature(lang_items, box_syntax, start, libc)] +#![feature(lang_items, box_syntax, start, libc, core_intrinsics)] #![no_std] +use core::intrinsics; extern crate libc; -extern { - fn abort() -> !; -} - #[lang = "owned_box"] pub struct Box(*mut T); @@ -34,7 +31,7 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { // Check if `malloc` failed: if p as usize == 0 { - abort(); + intrinsics::abort(); } p @@ -58,7 +55,7 @@ fn main(argc: isize, argv: *const *const u8) -> isize { } #[lang = "eh_personality"] extern fn rust_eh_personality() {} -#[lang = "panic_fmt"] extern fn rust_begin_panic() -> ! { loop {} } +#[lang = "panic_fmt"] extern fn rust_begin_panic() -> ! { unsafe { intrinsics::abort() } } # #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {} # #[no_mangle] pub extern fn rust_eh_register_frames () {} # #[no_mangle] pub extern fn rust_eh_unregister_frames () {} diff --git a/src/doc/book/no-stdlib.md b/src/doc/book/no-stdlib.md index a06de35c0ce6..79f0593be178 100644 --- a/src/doc/book/no-stdlib.md +++ b/src/doc/book/no-stdlib.md @@ -37,9 +37,10 @@ The function marked `#[start]` is passed the command line parameters in the same format as C: ```rust,ignore -#![feature(lang_items)] +#![feature(lang_items, core_intrinsics)] #![feature(start)] #![no_std] +use core::intrinsics; // Pull in the system libc library for what crt0.o likely requires. extern crate libc; @@ -69,7 +70,7 @@ pub extern fn rust_eh_unwind_resume() { pub extern fn rust_begin_panic(_msg: core::fmt::Arguments, _file: &'static str, _line: u32) -> ! { - loop {} + unsafe { intrinsics::abort() } } ``` @@ -79,10 +80,11 @@ correct ABI and the correct name, which requires overriding the compiler's name mangling too: ```rust,ignore -#![feature(lang_items)] +#![feature(lang_items, core_intrinsics)] #![feature(start)] #![no_std] #![no_main] +use core::intrinsics; // Pull in the system libc library for what crt0.o likely requires. extern crate libc; @@ -112,7 +114,7 @@ pub extern fn rust_eh_unwind_resume() { pub extern fn rust_begin_panic(_msg: core::fmt::Arguments, _file: &'static str, _line: u32) -> ! { - loop {} + unsafe { intrinsics::abort() } } ``` diff --git a/src/doc/book/procedural-macros.md b/src/doc/book/procedural-macros.md new file mode 100644 index 000000000000..37d3d20c06d9 --- /dev/null +++ b/src/doc/book/procedural-macros.md @@ -0,0 +1,213 @@ +% Procedural Macros (and custom Derive) + +As you've seen throughout the rest of the book, Rust provides a mechanism +called "derive" that lets you implement traits easily. For example, + +```rust +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} +``` + +is a lot simpler than + +```rust +struct Point { + x: i32, + y: i32, +} + +use std::fmt; + +impl fmt::Debug for Point { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y) + } +} +``` + +Rust includes several traits that you can derive, but it also lets you define +your own. We can accomplish this task through a feature of Rust called +"procedural macros." Eventually, procedural macros will allow for all sorts of +advanced metaprogramming in Rust, but today, they're only for custom derive. + +Let's build a very simple trait, and derive it with custom derive. + +## Hello World + +So the first thing we need to do is start a new crate for our project. + +```bash +$ cargo new --bin hello-world +``` + +All we want is to be able to call `hello_world()` on a derived type. Something +like this: + +```rust,ignore +#[derive(HelloWorld)] +struct Pancakes; + +fn main() { + Pancakes::hello_world(); +} +``` + +With some kind of nice output, like `Hello, World! My name is Pancakes.`. + +Let's go ahead and write up what we think our macro will look like from a user +perspective. In `src/main.rs` we write: + +```rust,ignore +#[macro_use] +extern crate hello_world_derive; + +trait HelloWorld { + fn hello_world(); +} + +#[derive(HelloWorld)] +struct FrenchToast; + +#[derive(HelloWorld)] +struct Waffles; + +fn main() { + FrenchToast::hello_world(); + Waffles::hello_world(); +} +``` + +Great. So now we just need to actually write the procedural macro. At the +moment, procedural macros need to be in their own crate. Eventually, this +restriction may be lifted, but for now, it's required. As such, there's a +convention; for a crate named `foo`, a custom derive procedural macro is called +`foo-derive`. Let's start a new crate called `hello-world-derive` inside our +`hello-world` project. + +```bash +$ cargo new hello-world-derive +``` + +To make sure that our `hello-world` crate is able to find this new crate we've +created, we'll add it to our toml: + +```toml +[dependencies] +hello-world-derive = { path = "hello-world-derive" } +``` + +As for our the source of our `hello-world-derive` crate, here's an example: + +```rust,ignore +extern crate proc_macro; +extern crate syn; +#[macro_use] +extern crate quote; + +use proc_macro::TokenStream; + +#[proc_macro_derive(HelloWorld)] +pub fn hello_world(input: TokenStream) -> TokenStream { + // Construct a string representation of the type definition + let s = input.to_string(); + + // Parse the string representation + let ast = syn::parse_macro_input(&s).unwrap(); + + // Build the impl + let gen = impl_hello_world(&ast); + + // Return the generated impl + gen.parse().unwrap() +} +``` + +So there is a lot going on here. We have introduced two new crates: [`syn`] and +[`quote`]. As you may have noticed, `input: TokenSteam` is immediately converted +to a `String`. This `String` is a string representation of the Rust code for which +we are deriving `HelloWorld` for. At the moment, the only thing you can do with a +`TokenStream` is convert it to a string. A richer API will exist in the future. + +So what we really need is to be able to _parse_ Rust code into something +usable. This is where `syn` comes to play. `syn` is a crate for parsing Rust +code. The other crate we've introduced is `quote`. It's essentially the dual of +`syn` as it will make generating Rust code really easy. We could write this +stuff on our own, but it's much simpler to use these libraries. Writing a full +parser for Rust code is no simple task. + +[`syn`]: https://crates.io/crates/syn +[`quote`]: https://crates.io/crates/quote + +The comments seem to give us a pretty good idea of our overall strategy. We +are going to take a `String` of the Rust code for the type we are deriving, parse +it using `syn`, construct the implementation of `hello_world` (using `quote`), +then pass it back to Rust compiler. + +One last note: you'll see some `unwrap()`s there. If you want to provide an +error for a procedural macro, then you should `panic!` with the error message. +In this case, we're keeping it as simple as possible. + +Great, so let's write `impl_hello_world(&ast)`. + +```rust,ignore +fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens { + let name = &ast.ident; + quote! { + impl HelloWorld for #name { + fn hello_world() { + println!("Hello, World! My name is {}", stringify!(#name)); + } + } + } +} +``` + +So this is where quotes comes in. The `ast` argument is a struct that gives us +a representation of our type (which can be either a `struct` or an `enum`). +Check out the [docs](https://docs.rs/syn/0.10.5/syn/struct.MacroInput.html), +there is some useful information there. We are able to get the name of the +type using `ast.ident`. The `quote!` macro let's us write up the Rust code +that we wish to return and convert it into `Tokens`. `quote!` let's us use some +really cool templating mechanics; we simply write `#name` and `quote!` will +replace it with the variable named `name`. You can even do some repetition +similar to regular macros work. You should check out the +[docs](https://docs.rs/quote) for a good introduction. + +So I think that's it. Oh, well, we do need to add dependencies for `syn` and +`quote` in the `cargo.toml` for `hello-world-derive`. + +```toml +[dependencies] +syn = "0.10.5" +quote = "0.3.10" +``` + +That should be it. Let's try to compile `hello-world`. + +```bash +error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type + --> hello-world-derive/src/lib.rs:8:3 + | +8 | #[proc_macro_derive(HelloWorld)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``` + +Oh, so it appears that we need to declare that our `hello-world-derive` crate is +a `proc-macro` crate type. How do we do this? Like this: + +```toml +[lib] +proc-macro = true +``` + +Ok so now, let's compile `hello-world`. Executing `cargo run` now yields: + +```bash +Hello, World! My name is FrenchToast +Hello, World! My name is Waffles +``` + +We've done it! diff --git a/src/doc/book/strings.md b/src/doc/book/strings.md index 6af15d876836..a2146b669e3c 100644 --- a/src/doc/book/strings.md +++ b/src/doc/book/strings.md @@ -163,8 +163,8 @@ let hachi = &dog[0..2]; with this error: ```text -thread 'main' panicked at 'index 0 and/or 2 in `忠犬ハチ公` do not lie on -character boundary' +thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '忠' +(bytes 0..3) of `忠犬ハチ公`' ``` ## Concatenation diff --git a/src/doc/nomicon/coercions.md b/src/doc/nomicon/coercions.md index 6a9ebd6edf8f..b699946cecaa 100644 --- a/src/doc/nomicon/coercions.md +++ b/src/doc/nomicon/coercions.md @@ -17,6 +17,7 @@ Coercion is allowed between the following types: * `&T` to `*const T` * `&mut T` to `*mut T` * Unsizing: `T` to `U` if `T` implements `CoerceUnsized` +* Deref coercion: Expression `&x` of type `&T` to `&*x` of type `&U` if `T` derefs to `U` (i.e. `T: Deref`) `CoerceUnsized> for Pointer where T: Unsize` is implemented for all pointer types (including smart pointers like Box and Rc). Unsize is @@ -27,8 +28,9 @@ only implemented automatically, and enables the following transformations: * `Foo<..., T, ...>` => `Foo<..., U, ...>` where: * `T: Unsize` * `Foo` is a struct - * Only the last field of `Foo` has type `T` + * Only the last field of `Foo` has type involving `T` * `T` is not part of the type of any other fields + * `Bar: Unsize>`, if the last field of `Foo` has type `Bar` Coercions occur at a *coercion site*. Any location that is explicitly typed will cause a coercion to its type. If inference is necessary, the coercion will diff --git a/src/doc/nomicon/dropck.md b/src/doc/nomicon/dropck.md index ad7c65032c76..f54933827b33 100644 --- a/src/doc/nomicon/dropck.md +++ b/src/doc/nomicon/dropck.md @@ -125,7 +125,7 @@ is that some Drop implementations will not access borrowed data even though their type gives them the capability for such access. For example, this variant of the above `Inspector` example will never -accessed borrowed data: +access borrowed data: ```rust,ignore struct Inspector<'a>(&'a u8, &'static str); diff --git a/src/doc/nomicon/unbounded-lifetimes.md b/src/doc/nomicon/unbounded-lifetimes.md index 1f2961b58613..1f3693f542f3 100644 --- a/src/doc/nomicon/unbounded-lifetimes.md +++ b/src/doc/nomicon/unbounded-lifetimes.md @@ -11,7 +11,7 @@ lifetime can be regarded as `'static`. Almost no reference is `'static`, so this is probably wrong. `transmute` and `transmute_copy` are the two other primary offenders. One should endeavor to -bound an unbounded lifetime as quick as possible, especially across function +bound an unbounded lifetime as quickly as possible, especially across function boundaries. Given a function, any output lifetimes that don't derive from inputs are diff --git a/src/doc/reference.md b/src/doc/reference.md index 9898c31282c3..4112b328f612 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -555,26 +555,24 @@ mod a { # fn main() {} ``` -# Syntax extensions +# Macros A number of minor features of Rust are not central enough to have their own syntax, and yet are not implementable as functions. Instead, they are given names, and invoked through a consistent syntax: `some_extension!(...)`. -Users of `rustc` can define new syntax extensions in two ways: - -* [Compiler plugins][plugin] can include arbitrary Rust code that - manipulates syntax trees at compile time. Note that the interface - for compiler plugins is considered highly unstable. +Users of `rustc` can define new macros in two ways: * [Macros](book/macros.html) define new syntax in a higher-level, declarative way. +* [Procedural Macros][procedural macros] can be used to implement custom derive. + +And one unstable way: [compiler plugins][plugin]. ## Macros `macro_rules` allows users to define syntax extension in a declarative way. We -call such extensions "macros by example" or simply "macros" — to be distinguished -from the "procedural macros" defined in [compiler plugins][plugin]. +call such extensions "macros by example" or simply "macros". Currently, macros can expand to expressions, statements, items, or patterns. @@ -652,6 +650,28 @@ Rust syntax is restricted in two ways: [RFC 550]: https://github.com/rust-lang/rfcs/blob/master/text/0550-macro-future-proofing.md +## Procedrual Macros + +"Procedrual macros" are the second way to implement a macro. For now, the only +thing they can be used for is to implement derive on your own types. See +[the book][procedural macros] for a tutorial. + +Procedural macros involve a few different parts of the language and its +standard libraries. First is the `proc_macro` crate, included with Rust, +that defines an interface for building a procedrual macro. The +`#[proc_macro_derive(Foo)]` attribute is used to mark the the deriving +function. This function must have the type signature: + +```rust,ignore +use proc_macro::TokenStream; + +#[proc_macro_derive(Hello)] +pub fn hello_world(input: TokenStream) -> TokenStream +``` + +Finally, procedural macros must be in their own crate, with the `proc-macro` +crate type. + # Crates and source files Although Rust, like any other language, can be implemented by an interpreter as @@ -2319,6 +2339,9 @@ impl PartialEq for Foo { } ``` +You can implement `derive` for your own type through [procedural +macros](#procedural-macros). + ### Compiler Features Certain aspects of Rust may be implemented in the compiler, but they're not @@ -4122,6 +4145,16 @@ be ignored in favor of only building the artifacts specified by command line. in dynamic libraries. This form of output is used to produce statically linked executables as well as `staticlib` outputs. +* `--crate-type=proc-macro`, `#[crate_type = "proc-macro"]` - The output + produced is not specified, but if a `-L` path is provided to it then the + compiler will recognize the output artifacts as a macro and it can be loaded + for a program. If a crate is compiled with the `proc-macro` crate type it + will forbid exporting any items in the crate other than those functions + tagged `#[proc_macro_derive]` and those functions must also be placed at the + crate root. Finally, the compiler will automatically set the + `cfg(proc_macro)` annotation whenever any crate type of a compilation is the + `proc-macro` crate type. + Note that these outputs are stackable in the sense that if multiple are specified, then the compiler will produce each form of output at once without having to recompile. However, this only applies for outputs specified by the @@ -4299,3 +4332,4 @@ that have since been removed): [ffi]: book/ffi.html [plugin]: book/compiler-plugins.html +[procedural macros]: book/procedural-macros.html diff --git a/src/etc/unicode.py b/src/etc/unicode.py index bddc83f63d25..e5389c112e81 100755 --- a/src/etc/unicode.py +++ b/src/etc/unicode.py @@ -23,10 +23,7 @@ # Since this should not require frequent updates, we just store this # out-of-line and check the unicode.rs file into git. -import fileinput, re, os, sys, operator - -bytes_old = 0 -bytes_new = 0 +import fileinput, re, os, sys, operator, math preamble = '''// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at @@ -362,7 +359,23 @@ fn trie_lookup_range_table(c: char, r: &'static BoolTrie) -> bool { let leaf = r.r5[((child as usize) << 6) + ((c >> 6) & 0x3f)]; trie_range_leaf(c, r.r6[leaf as usize]) } -}\n +} + +pub struct SmallBoolTrie { + r1: &'static [u8], // first level + r2: &'static [u64], // leaves +} + +impl SmallBoolTrie { + fn lookup(&self, c: char) -> bool { + let c = c as usize; + match self.r1.get(c >> 6) { + Some(&child) => trie_range_leaf(c, self.r2[child as usize]), + None => false, + } + } +} + """) def compute_trie(rawdata, chunksize): @@ -379,8 +392,6 @@ def compute_trie(rawdata, chunksize): return (root, child_data) def emit_bool_trie(f, name, t_data, is_pub=True): - global bytes_old, bytes_new - bytes_old += 8 * len(t_data) CHUNK = 64 rawdata = [False] * 0x110000 for (lo, hi) in t_data: @@ -433,15 +444,50 @@ def emit_bool_trie(f, name, t_data, is_pub=True): f.write("\n ],\n") f.write(" };\n\n") - bytes_new += 256 + 992 + 256 + 8 * len(r3) + len(r5) + 8 * len(r6) + +def emit_small_bool_trie(f, name, t_data, is_pub=True): + last_chunk = max(int(hi / 64) for (lo, hi) in t_data) + n_chunks = last_chunk + 1 + chunks = [0] * n_chunks + for (lo, hi) in t_data: + for cp in range(lo, hi + 1): + if int(cp / 64) >= len(chunks): + print(cp, int(cp / 64), len(chunks), lo, hi) + chunks[int(cp / 64)] |= 1 << (cp & 63) + + pub_string = "" + if is_pub: + pub_string = "pub " + f.write(" %sconst %s: &'static super::SmallBoolTrie = &super::SmallBoolTrie {\n" + % (pub_string, name)) + + (r1, r2) = compute_trie(chunks, 1) + + f.write(" r1: &[\n") + data = ','.join(str(node) for node in r1) + format_table_content(f, data, 12) + f.write("\n ],\n") + + f.write(" r2: &[\n") + data = ','.join('0x%016x' % node for node in r2) + format_table_content(f, data, 12) + f.write("\n ],\n") + + f.write(" };\n\n") def emit_property_module(f, mod, tbl, emit): f.write("pub mod %s {\n" % mod) for cat in sorted(emit): - emit_bool_trie(f, "%s_table" % cat, tbl[cat]) - f.write(" pub fn %s(c: char) -> bool {\n" % cat) - f.write(" super::trie_lookup_range_table(c, %s_table)\n" % cat) - f.write(" }\n\n") + if cat in ["Cc", "White_Space", "Pattern_White_Space"]: + emit_small_bool_trie(f, "%s_table" % cat, tbl[cat]) + f.write(" pub fn %s(c: char) -> bool {\n" % cat) + f.write(" %s_table.lookup(c)\n" % cat) + f.write(" }\n\n") + else: + emit_bool_trie(f, "%s_table" % cat, tbl[cat]) + f.write(" pub fn %s(c: char) -> bool {\n" % cat) + f.write(" super::trie_lookup_range_table(c, %s_table)\n" % cat) + f.write(" }\n\n") f.write("}\n\n") def emit_conversions_module(f, to_upper, to_lower, to_title): @@ -543,4 +589,3 @@ pub const UNICODE_VERSION: (u64, u64, u64) = (%s, %s, %s); # normalizations and conversions module emit_norm_module(rf, canon_decomp, compat_decomp, combines, norm_props) emit_conversions_module(rf, to_upper, to_lower, to_title) - #print 'bytes before = %d, bytes after = %d' % (bytes_old, bytes_new) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index e1a240a0d2eb..459dc94f3368 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -708,7 +708,7 @@ impl Arc { } #[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Arc { +unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { /// Drops the `Arc`. /// /// This will decrement the strong reference count. If the strong reference @@ -736,7 +736,6 @@ impl Drop for Arc { /// drop(foo); // Doesn't print anything /// drop(foo2); // Prints "dropped!" /// ``` - #[unsafe_destructor_blind_to_params] #[inline] fn drop(&mut self) { // Because `fetch_sub` is already atomic, we do not need to synchronize diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index addb056f5342..5409ade29236 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -587,7 +587,7 @@ impl FusedIterator for Box {} /// ``` #[rustc_paren_sugar] #[unstable(feature = "fnbox", - reason = "will be deprecated if and when Box becomes usable", issue = "28796")] + reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] pub trait FnBox { type Output; @@ -595,7 +595,7 @@ pub trait FnBox { } #[unstable(feature = "fnbox", - reason = "will be deprecated if and when Box becomes usable", issue = "28796")] + reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl FnBox for F where F: FnOnce { @@ -607,7 +607,7 @@ impl FnBox for F } #[unstable(feature = "fnbox", - reason = "will be deprecated if and when Box becomes usable", issue = "28796")] + reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl<'a, A, R> FnOnce for Box + 'a> { type Output = R; @@ -617,7 +617,7 @@ impl<'a, A, R> FnOnce for Box + 'a> { } #[unstable(feature = "fnbox", - reason = "will be deprecated if and when Box becomes usable", issue = "28796")] + reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl<'a, A, R> FnOnce for Box + Send + 'a> { type Output = R; diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f9dfdc0e0753..c67106cf57aa 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -70,7 +70,7 @@ test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] #![no_std] #![needs_allocator] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(allocator)] #![feature(box_syntax)] @@ -79,9 +79,10 @@ #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(custom_attribute)] -#![feature(dropck_parametricity)] +#![feature(dropck_eyepatch)] #![cfg_attr(not(test), feature(exact_size_is_empty))] #![feature(fundamental)] +#![feature(generic_param_attrs)] #![feature(lang_items)] #![feature(needs_allocator)] #![feature(optin_builtin_traits)] diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index f23ea0ea8bf7..357a2724e002 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -539,8 +539,7 @@ impl RawVec { } } -impl Drop for RawVec { - #[unsafe_destructor_blind_to_params] +unsafe impl<#[may_dangle] T> Drop for RawVec { /// Frees the memory owned by the RawVec *without* trying to Drop its contents. fn drop(&mut self) { let elem_size = mem::size_of::(); diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 86f8c746646a..010e378ef2f4 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -644,7 +644,7 @@ impl Deref for Rc { } #[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Rc { +unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { /// Drops the `Rc`. /// /// This will decrement the strong reference count. If the strong reference @@ -672,7 +672,6 @@ impl Drop for Rc { /// drop(foo); // Doesn't print anything /// drop(foo2); // Prints "dropped!" /// ``` - #[unsafe_destructor_blind_to_params] fn drop(&mut self) { unsafe { let ptr = *self.ptr; diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index f2df393ad777..241f8149d24d 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -16,7 +16,7 @@ reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(allocator)] #![feature(libc)] #![feature(staged_api)] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 5f0b637656b7..de2b75f62b68 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -12,7 +12,7 @@ #![crate_type = "rlib"] #![no_std] #![allocator] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![unstable(feature = "alloc_system", reason = "this library is unlikely to be stabilized in its current \ form or name", diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 174a93ed23e0..a3cfc15895eb 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -26,14 +26,14 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", test(no_crate_inject, attr(deny(warnings))))] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(alloc)] #![feature(core_intrinsics)] +#![feature(dropck_eyepatch)] #![feature(heap_api)] -#![feature(heap_api)] +#![feature(generic_param_attrs)] #![feature(staged_api)] -#![feature(dropck_parametricity)] #![cfg_attr(test, feature(test))] #![allow(deprecated)] @@ -258,8 +258,7 @@ impl TypedArena { } } -impl Drop for TypedArena { - #[unsafe_destructor_blind_to_params] +unsafe impl<#[may_dangle] T> Drop for TypedArena { fn drop(&mut self) { unsafe { // Determine how much was filled. diff --git a/src/libcollections/binary_heap.rs b/src/libcollections/binary_heap.rs index c5d5ad27d230..7d245f79f699 100644 --- a/src/libcollections/binary_heap.rs +++ b/src/libcollections/binary_heap.rs @@ -153,8 +153,7 @@ use core::ops::{Deref, DerefMut}; use core::iter::{FromIterator, FusedIterator}; -use core::mem::swap; -use core::mem::size_of; +use core::mem::{swap, size_of}; use core::ptr; use core::fmt; @@ -226,12 +225,15 @@ pub struct BinaryHeap { #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] pub struct PeekMut<'a, T: 'a + Ord> { heap: &'a mut BinaryHeap, + sift: bool, } #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] impl<'a, T: Ord> Drop for PeekMut<'a, T> { fn drop(&mut self) { - self.heap.sift_down(0); + if self.sift { + self.heap.sift_down(0); + } } } @@ -250,6 +252,16 @@ impl<'a, T: Ord> DerefMut for PeekMut<'a, T> { } } +impl<'a, T: Ord> PeekMut<'a, T> { + /// Removes the peeked value from the heap and returns it. + #[unstable(feature = "binary_heap_peek_mut_pop", issue = "38863")] + pub fn pop(mut this: PeekMut<'a, T>) -> T { + let value = this.heap.pop().unwrap(); + this.sift = false; + value + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Clone for BinaryHeap { fn clone(&self) -> Self { @@ -385,7 +397,10 @@ impl BinaryHeap { if self.is_empty() { None } else { - Some(PeekMut { heap: self }) + Some(PeekMut { + heap: self, + sift: true, + }) } } diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index 788236c24d06..85e1e133b7d9 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -137,8 +137,7 @@ pub struct BTreeMap { } #[stable(feature = "btree_drop", since = "1.7.0")] -impl Drop for BTreeMap { - #[unsafe_destructor_blind_to_params] +unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for BTreeMap { fn drop(&mut self) { unsafe { for _ in ptr::read(self).into_iter() { @@ -1991,11 +1990,11 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); - /// let s = "hoho".to_owned(); + /// let s = "hoho".to_string(); /// /// map.entry("poneyland").or_insert_with(|| s); /// - /// assert_eq!(map["poneyland"], "hoho".to_owned()); + /// assert_eq!(map["poneyland"], "hoho".to_string()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn or_insert_with V>(self, default: F) -> &'a mut V { diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 68b067012d3f..561d8860dc88 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -27,7 +27,7 @@ test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] #![cfg_attr(test, allow(deprecated))] // rand -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(alloc)] #![feature(allow_internal_unstable)] @@ -35,16 +35,18 @@ #![feature(box_syntax)] #![cfg_attr(not(test), feature(char_escape_debug))] #![feature(core_intrinsics)] -#![feature(dropck_parametricity)] +#![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] #![feature(fmt_internals)] #![feature(fused)] +#![feature(generic_param_attrs)] #![feature(heap_api)] #![feature(inclusive_range)] #![feature(lang_items)] #![feature(nonzero)] #![feature(pattern)] #![feature(placement_in)] +#![feature(placement_in_syntax)] #![feature(placement_new_protocol)] #![feature(shared)] #![feature(slice_get_slice)] diff --git a/src/libcollections/linked_list.rs b/src/libcollections/linked_list.rs index 310855090885..7f913d4afe47 100644 --- a/src/libcollections/linked_list.rs +++ b/src/libcollections/linked_list.rs @@ -726,8 +726,7 @@ impl LinkedList { } #[stable(feature = "rust1", since = "1.0.0")] -impl Drop for LinkedList { - #[unsafe_destructor_blind_to_params] +unsafe impl<#[may_dangle] T> Drop for LinkedList { fn drop(&mut self) { while let Some(_) = self.pop_front_node() {} } diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index b5e666922053..805021516db7 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -423,7 +423,7 @@ impl [T] { core_slice::SliceExt::get_unchecked_mut(self, index) } - /// Returns an raw pointer to the slice's buffer. + /// Returns a raw pointer to the slice's buffer. /// /// The caller must ensure that the slice outlives the pointer this /// function returns, or else it will end up pointing to garbage. diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index f2ef54f6e567..27cf0268c99b 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -77,7 +77,7 @@ use core::hash::{self, Hash}; use core::intrinsics::{arith_offset, assume}; use core::iter::{FromIterator, FusedIterator, TrustedLen}; use core::mem; -use core::ops::{Index, IndexMut}; +use core::ops::{InPlace, Index, IndexMut, Place, Placer}; use core::ops; use core::ptr; use core::ptr::Shared; @@ -370,7 +370,8 @@ impl Vec { /// * `capacity` needs to be the capacity that the pointer was allocated with. /// /// Violating these may cause problems like corrupting the allocator's - /// internal datastructures. + /// internal datastructures. For example it is **not** safe + /// to build a `Vec` from a pointer to a C `char` array and a `size_t`. /// /// The ownership of `ptr` is effectively transferred to the /// `Vec` which may then deallocate, reallocate or change the @@ -1246,6 +1247,29 @@ impl Vec { pub fn extend_from_slice(&mut self, other: &[T]) { self.spec_extend(other.iter()) } + + /// Returns a place for insertion at the back of the `Vec`. + /// + /// Using this method with placement syntax is equivalent to [`push`](#method.push), + /// but may be more efficient. + /// + /// # Examples + /// + /// ``` + /// #![feature(collection_placement)] + /// #![feature(placement_in_syntax)] + /// + /// let mut vec = vec![1, 2]; + /// vec.place_back() <- 3; + /// vec.place_back() <- 4; + /// assert_eq!(&vec, &[1, 2, 3, 4]); + /// ``` + #[unstable(feature = "collection_placement", + reason = "placement protocol is subject to change", + issue = "30172")] + pub fn place_back(&mut self) -> PlaceBack { + PlaceBack { vec: self } + } } // Set the length of the vec when the `SetLenOnDrop` value goes out of scope. @@ -1763,8 +1787,7 @@ impl Ord for Vec { } #[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Vec { - #[unsafe_destructor_blind_to_params] +unsafe impl<#[may_dangle] T> Drop for Vec { fn drop(&mut self) { unsafe { // use drop for [T] @@ -2033,8 +2056,7 @@ impl Clone for IntoIter { } #[stable(feature = "rust1", since = "1.0.0")] -impl Drop for IntoIter { - #[unsafe_destructor_blind_to_params] +unsafe impl<#[may_dangle] T> Drop for IntoIter { fn drop(&mut self) { // destroy the remaining elements for _x in self.by_ref() {} @@ -2119,3 +2141,52 @@ impl<'a, T> ExactSizeIterator for Drain<'a, T> { #[unstable(feature = "fused", issue = "35602")] impl<'a, T> FusedIterator for Drain<'a, T> {} + +/// A place for insertion at the back of a `Vec`. +/// +/// See [`Vec::place_back`](struct.Vec.html#method.place_back) for details. +#[must_use = "places do nothing unless written to with `<-` syntax"] +#[unstable(feature = "collection_placement", + reason = "struct name and placement protocol are subject to change", + issue = "30172")] +pub struct PlaceBack<'a, T: 'a> { + vec: &'a mut Vec, +} + +#[unstable(feature = "collection_placement", + reason = "placement protocol is subject to change", + issue = "30172")] +impl<'a, T> Placer for PlaceBack<'a, T> { + type Place = PlaceBack<'a, T>; + + fn make_place(self) -> Self { + // This will panic or abort if we would allocate > isize::MAX bytes + // or if the length increment would overflow for zero-sized types. + if self.vec.len == self.vec.buf.cap() { + self.vec.buf.double(); + } + self + } +} + +#[unstable(feature = "collection_placement", + reason = "placement protocol is subject to change", + issue = "30172")] +impl<'a, T> Place for PlaceBack<'a, T> { + fn pointer(&mut self) -> *mut T { + unsafe { self.vec.as_mut_ptr().offset(self.vec.len as isize) } + } +} + +#[unstable(feature = "collection_placement", + reason = "placement protocol is subject to change", + issue = "30172")] +impl<'a, T> InPlace for PlaceBack<'a, T> { + type Owner = &'a mut T; + + unsafe fn finalize(mut self) -> &'a mut T { + let ptr = self.pointer(); + self.vec.len += 1; + &mut *ptr + } +} diff --git a/src/libcollections/vec_deque.rs b/src/libcollections/vec_deque.rs index 67621b860bf3..76e44c815793 100644 --- a/src/libcollections/vec_deque.rs +++ b/src/libcollections/vec_deque.rs @@ -69,8 +69,7 @@ impl Clone for VecDeque { } #[stable(feature = "rust1", since = "1.0.0")] -impl Drop for VecDeque { - #[unsafe_destructor_blind_to_params] +unsafe impl<#[may_dangle] T> Drop for VecDeque { fn drop(&mut self) { let (front, back) = self.as_mut_slices(); unsafe { @@ -1228,9 +1227,8 @@ impl VecDeque { self.pop_front() } - /// Inserts an element at `index` within the `VecDeque`. Whichever - /// end is closer to the insertion point will be moved to make room, - /// and all the affected elements will be moved to new positions. + /// Inserts an element at `index` within the `VecDeque`, shifting all elements with indices + /// greater than or equal to `index` towards the back. /// /// Element at index 0 is the front of the queue. /// @@ -1239,14 +1237,19 @@ impl VecDeque { /// Panics if `index` is greater than `VecDeque`'s length /// /// # Examples + /// /// ``` /// use std::collections::VecDeque; /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(10); - /// buf.push_back(12); - /// buf.insert(1, 11); - /// assert_eq!(Some(&11), buf.get(1)); + /// let mut vec_deque = VecDeque::new(); + /// vec_deque.push_back('a'); + /// vec_deque.push_back('b'); + /// vec_deque.push_back('c'); + /// + /// vec_deque.insert(1, 'd'); + /// + /// let vec = vec_deque.into_iter().collect::>(); + /// assert_eq!(vec, ['a', 'd', 'b', 'c']); /// ``` #[stable(feature = "deque_extras_15", since = "1.5.0")] pub fn insert(&mut self, index: usize, value: T) { diff --git a/src/libcollectionstest/binary_heap.rs b/src/libcollectionstest/binary_heap.rs index 9cd63d879318..1df341d1fc28 100644 --- a/src/libcollectionstest/binary_heap.rs +++ b/src/libcollectionstest/binary_heap.rs @@ -9,7 +9,7 @@ // except according to those terms. use std::collections::BinaryHeap; -use std::collections::binary_heap::Drain; +use std::collections::binary_heap::{Drain, PeekMut}; #[test] fn test_iterator() { @@ -94,6 +94,19 @@ fn test_peek_mut() { assert_eq!(heap.peek(), Some(&9)); } +#[test] +fn test_peek_mut_pop() { + let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; + let mut heap = BinaryHeap::from(data); + assert_eq!(heap.peek(), Some(&10)); + { + let mut top = heap.peek_mut().unwrap(); + *top -= 2; + assert_eq!(PeekMut::pop(top), 8); + } + assert_eq!(heap.peek(), Some(&9)); +} + #[test] fn test_push() { let mut heap = BinaryHeap::from(vec![2, 4, 9]); diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs index d4fb5ea03ade..bec3965a9589 100644 --- a/src/libcollectionstest/lib.rs +++ b/src/libcollectionstest/lib.rs @@ -11,8 +11,10 @@ #![deny(warnings)] #![feature(binary_heap_extras)] +#![feature(binary_heap_peek_mut_pop)] #![feature(box_syntax)] #![feature(btree_range)] +#![feature(collection_placement)] #![feature(collections)] #![feature(collections_bound)] #![feature(const_fn)] @@ -20,6 +22,7 @@ #![feature(enumset)] #![feature(exact_size_is_empty)] #![feature(pattern)] +#![feature(placement_in_syntax)] #![feature(rand)] #![feature(repeat_str)] #![feature(step_by)] diff --git a/src/libcollectionstest/str.rs b/src/libcollectionstest/str.rs index 384579ce6b8b..5e685847e3c8 100644 --- a/src/libcollectionstest/str.rs +++ b/src/libcollectionstest/str.rs @@ -383,17 +383,29 @@ tempus vel, gravida nec quam."; // check the panic includes the prefix of the sliced string #[test] -#[should_panic(expected="Lorem ipsum dolor sit amet")] +#[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")] fn test_slice_fail_truncated_1() { &LOREM_PARAGRAPH[..1024]; } // check the truncation in the panic message #[test] -#[should_panic(expected="luctus, im`[...] do not lie on character boundary")] +#[should_panic(expected="luctus, im`[...]")] fn test_slice_fail_truncated_2() { &LOREM_PARAGRAPH[..1024]; } +#[test] +#[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")] +fn test_slice_fail_boundary_1() { + &"abcαβγ"[4..]; +} + +#[test] +#[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")] +fn test_slice_fail_boundary_2() { + &"abcαβγ"[2..6]; +} + #[test] fn test_slice_from() { assert_eq!(&"abcd"[0..], "abcd"); diff --git a/src/libcollectionstest/vec.rs b/src/libcollectionstest/vec.rs index 3bc1321d7565..6d0f1eaffaa7 100644 --- a/src/libcollectionstest/vec.rs +++ b/src/libcollectionstest/vec.rs @@ -12,6 +12,7 @@ use std::ascii::AsciiExt; use std::borrow::Cow; use std::iter::{FromIterator, repeat}; use std::mem::size_of; +use std::panic; use std::vec::{Drain, IntoIter}; use test::Bencher; @@ -615,6 +616,24 @@ fn assert_covariance() { } } +#[test] +fn test_placement() { + let mut vec = vec![1]; + assert_eq!(vec.place_back() <- 2, &2); + assert_eq!(vec.len(), 2); + assert_eq!(vec.place_back() <- 3, &3); + assert_eq!(vec.len(), 3); + assert_eq!(&vec, &[1, 2, 3]); +} + +#[test] +fn test_placement_panic() { + let mut vec = vec![1, 2, 3]; + fn mkpanic() -> usize { panic!() } + let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { vec.place_back() <- mkpanic(); })); + assert_eq!(vec.len(), 3); +} + #[bench] fn bench_new(b: &mut Bencher) { b.iter(|| { diff --git a/src/libcompiler_builtins/build.rs b/src/libcompiler_builtins/build.rs index 44aa08e24587..ab9a71e1ec6a 100644 --- a/src/libcompiler_builtins/build.rs +++ b/src/libcompiler_builtins/build.rs @@ -192,14 +192,12 @@ fn main() { if !target.contains("ios") { sources.extend(&["absvti2.c", - "addtf3.c", "addvti3.c", "ashlti3.c", "ashrti3.c", "clzti2.c", "cmpti2.c", "ctzti2.c", - "divtf3.c", "divti3.c", "ffsti2.c", "fixdfti.c", @@ -216,17 +214,13 @@ fn main() { "floatuntixf.c", "lshrti3.c", "modti3.c", - "multf3.c", "multi3.c", "mulvti3.c", "negti2.c", "negvti2.c", "parityti2.c", "popcountti2.c", - "powitf2.c", - "subtf3.c", "subvti3.c", - "trampoline_setup.c", "ucmpti2.c", "udivmodti4.c", "udivti3.c", @@ -242,10 +236,6 @@ fn main() { "atomic_thread_fence.c"]); } - if !target.contains("redox") && !target.contains("windows") { - sources.extend(&["emutls.c"]); - } - if target.contains("msvc") { if target.contains("x86_64") { sources.extend(&["x86_64/floatdidf.c", "x86_64/floatdisf.c", "x86_64/floatdixf.c"]); diff --git a/src/libcore/any.rs b/src/libcore/any.rs index eb0636e8576b..78f3cd5576e1 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -101,7 +101,7 @@ pub trait Any: 'static { /// /// fn main() { /// assert_eq!(is_string(&0), false); - /// assert_eq!(is_string(&"cookie monster".to_owned()), true); + /// assert_eq!(is_string(&"cookie monster".to_string()), true); /// } /// ``` #[unstable(feature = "get_type_id", @@ -154,7 +154,7 @@ impl Any { /// /// fn main() { /// is_string(&0); - /// is_string(&"cookie monster".to_owned()); + /// is_string(&"cookie monster".to_string()); /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -188,7 +188,7 @@ impl Any { /// /// fn main() { /// print_if_string(&0); - /// print_if_string(&"cookie monster".to_owned()); + /// print_if_string(&"cookie monster".to_string()); /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -219,7 +219,7 @@ impl Any { /// /// fn main() { /// let mut x = 10u32; - /// let mut s = "starlord".to_owned(); + /// let mut s = "starlord".to_string(); /// /// modify_if_u32(&mut x); /// modify_if_u32(&mut s); @@ -259,7 +259,7 @@ impl Any+Send { /// /// fn main() { /// is_string(&0); - /// is_string(&"cookie monster".to_owned()); + /// is_string(&"cookie monster".to_string()); /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -285,7 +285,7 @@ impl Any+Send { /// /// fn main() { /// print_if_string(&0); - /// print_if_string(&"cookie monster".to_owned()); + /// print_if_string(&"cookie monster".to_string()); /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -309,7 +309,7 @@ impl Any+Send { /// /// fn main() { /// let mut x = 10u32; - /// let mut s = "starlord".to_owned(); + /// let mut s = "starlord".to_string(); /// /// modify_if_u32(&mut x); /// modify_if_u32(&mut s); @@ -359,7 +359,7 @@ impl TypeId { /// /// fn main() { /// assert_eq!(is_string(&0), false); - /// assert_eq!(is_string(&"cookie monster".to_owned()), true); + /// assert_eq!(is_string(&"cookie monster".to_string()), true); /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/char.rs b/src/libcore/char.rs index c14ae6e08987..367422f55364 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -17,7 +17,7 @@ use char_private::is_printable; use convert::TryFrom; -use fmt; +use fmt::{self, Write}; use slice; use iter::FusedIterator; use mem::transmute; @@ -588,6 +588,16 @@ impl ExactSizeIterator for EscapeUnicode { #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for EscapeUnicode {} +#[stable(feature = "char_struct_display", since = "1.17.0")] +impl fmt::Display for EscapeUnicode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for c in self.clone() { + f.write_char(c)?; + } + Ok(()) + } +} + /// An iterator that yields the literal escape code of a `char`. /// /// This `struct` is created by the [`escape_default()`] method on [`char`]. See @@ -691,6 +701,16 @@ impl ExactSizeIterator for EscapeDefault { #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for EscapeDefault {} +#[stable(feature = "char_struct_display", since = "1.17.0")] +impl fmt::Display for EscapeDefault { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for c in self.clone() { + f.write_char(c)?; + } + Ok(()) + } +} + /// An iterator that yields the literal escape code of a `char`. /// /// This `struct` is created by the [`escape_debug()`] method on [`char`]. See its @@ -715,6 +735,13 @@ impl ExactSizeIterator for EscapeDebug { } #[unstable(feature = "fused", issue = "35602")] impl FusedIterator for EscapeDebug {} +#[stable(feature = "char_struct_display", since = "1.17.0")] +impl fmt::Display for EscapeDebug { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + /// An iterator over an iterator of bytes of the characters the bytes represent diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 3726eee9a93c..31a0cc688418 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1156,10 +1156,58 @@ extern "rust-intrinsic" { /// Returns the number of bits set in an integer type `T` pub fn ctpop(x: T) -> T; - /// Returns the number of leading bits unset in an integer type `T` + /// Returns the number of leading unset bits (zeroes) in an integer type `T`. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::ctlz; + /// + /// let x = 0b0001_1100_u8; + /// let num_leading = unsafe { ctlz(x) }; + /// assert_eq!(num_leading, 3); + /// ``` + /// + /// An `x` with value `0` will return the bit width of `T`. + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::ctlz; + /// + /// let x = 0u16; + /// let num_leading = unsafe { ctlz(x) }; + /// assert_eq!(num_leading, 16); + /// ``` pub fn ctlz(x: T) -> T; - /// Returns the number of trailing bits unset in an integer type `T` + /// Returns the number of trailing unset bits (zeroes) in an integer type `T`. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::cttz; + /// + /// let x = 0b0011_1000_u8; + /// let num_trailing = unsafe { cttz(x) }; + /// assert_eq!(num_trailing, 3); + /// ``` + /// + /// An `x` with value `0` will return the bit width of `T`: + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::cttz; + /// + /// let x = 0u16; + /// let num_trailing = unsafe { cttz(x) }; + /// assert_eq!(num_trailing, 16); + /// ``` pub fn cttz(x: T) -> T; /// Reverses the bytes in an integer type `T`. diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index ec590d2bd06f..91c09c553056 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -1612,7 +1612,7 @@ pub trait Iterator { /// Returns the maximum element of an iterator. /// - /// If the two elements are equally maximum, the latest element is + /// If several elements are equally maximum, the last element is /// returned. /// /// # Examples @@ -1638,7 +1638,7 @@ pub trait Iterator { /// Returns the minimum element of an iterator. /// - /// If the two elements are equally minimum, the first element is + /// If several elements are equally minimum, the first element is /// returned. /// /// # Examples @@ -1665,8 +1665,8 @@ pub trait Iterator { /// Returns the element that gives the maximum value from the /// specified function. /// - /// Returns the rightmost element if the comparison determines two elements - /// to be equally maximum. + /// If several elements are equally maximum, the last element is + /// returned. /// /// # Examples /// @@ -1690,8 +1690,8 @@ pub trait Iterator { /// Returns the element that gives the maximum value with respect to the /// specified comparison function. /// - /// Returns the rightmost element if the comparison determines two elements - /// to be equally maximum. + /// If several elements are equally maximum, the last element is + /// returned. /// /// # Examples /// @@ -1715,8 +1715,8 @@ pub trait Iterator { /// Returns the element that gives the minimum value from the /// specified function. /// - /// Returns the latest element if the comparison determines two elements - /// to be equally minimum. + /// If several elements are equally minimum, the first element is + /// returned. /// /// # Examples /// @@ -1739,8 +1739,8 @@ pub trait Iterator { /// Returns the element that gives the minimum value with respect to the /// specified comparison function. /// - /// Returns the latest element if the comparison determines two elements - /// to be equally minimum. + /// If several elements are equally minimum, the first element is + /// returned. /// /// # Examples /// diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index c5465549adf7..1e1271483006 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -260,7 +260,10 @@ impl IntoIterator for I { /// /// Iterators produce a series of values, and collections can also be thought /// of as a series of values. The `Extend` trait bridges this gap, allowing you -/// to extend a collection by including the contents of that iterator. +/// to extend a collection by including the contents of that iterator. When +/// extending a collection with an already existing key, that entry is updated +/// or, in the case of collections that permit multiple entries with equal +/// keys, that entry is inserted. /// /// # Examples /// @@ -670,6 +673,87 @@ macro_rules! float_sum_product { integer_sum_product! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize } float_sum_product! { f32 f64 } +/// An iterator adapter that produces output as long as the underlying +/// iterator produces `Result::Ok` values. +/// +/// If an error is encountered, the iterator stops and the error is +/// stored. The error may be recovered later via `reconstruct`. +struct ResultShunt { + iter: I, + error: Option, +} + +impl ResultShunt + where I: Iterator> +{ + /// Process the given iterator as if it yielded a `T` instead of a + /// `Result`. Any errors will stop the inner iterator and + /// the overall result will be an error. + pub fn process(iter: I, mut f: F) -> Result + where F: FnMut(&mut Self) -> U + { + let mut shunt = ResultShunt::new(iter); + let value = f(shunt.by_ref()); + shunt.reconstruct(value) + } + + fn new(iter: I) -> Self { + ResultShunt { + iter: iter, + error: None, + } + } + + /// Consume the adapter and rebuild a `Result` value. This should + /// *always* be called, otherwise any potential error would be + /// lost. + fn reconstruct(self, val: U) -> Result { + match self.error { + None => Ok(val), + Some(e) => Err(e), + } + } +} + +impl Iterator for ResultShunt + where I: Iterator> +{ + type Item = T; + + fn next(&mut self) -> Option { + match self.iter.next() { + Some(Ok(v)) => Some(v), + Some(Err(e)) => { + self.error = Some(e); + None + } + None => None, + } + } +} + +#[stable(feature = "iter_arith_traits_result", since="1.16.0")] +impl Sum> for Result + where T: Sum, +{ + fn sum(iter: I) -> Result + where I: Iterator>, + { + ResultShunt::process(iter, |i| i.sum()) + } +} + +#[stable(feature = "iter_arith_traits_result", since="1.16.0")] +impl Product> for Result + where T: Product, +{ + fn product(iter: I) -> Result + where I: Iterator>, + { + ResultShunt::process(iter, |i| i.product()) + } +} + /// An iterator that always continues to yield `None` when exhausted. /// /// Calling next on a fused iterator that has returned `None` once is guaranteed diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 2cb2f81fcffb..35324f7b5968 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -65,7 +65,7 @@ #![no_core] #![deny(missing_docs)] #![deny(missing_debug_implementations)] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(allow_internal_unstable)] #![feature(asm)] diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index b3f5363f5b15..ba52edb1ae83 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -404,10 +404,11 @@ macro_rules! write { /// use std::io::Write; /// /// let mut w = Vec::new(); +/// writeln!(&mut w).unwrap(); /// writeln!(&mut w, "test").unwrap(); /// writeln!(&mut w, "formatted {}", "arguments").unwrap(); /// -/// assert_eq!(&w[..], "test\nformatted arguments\n".as_bytes()); +/// assert_eq!(&w[..], "\ntest\nformatted arguments\n".as_bytes()); /// ``` /// /// A module can import both `std::fmt::Write` and `std::io::Write` and call `write!` on objects @@ -427,6 +428,9 @@ macro_rules! write { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] macro_rules! writeln { + ($dst:expr) => ( + write!($dst, "\n") + ); ($dst:expr, $fmt:expr) => ( write!($dst, concat!($fmt, "\n")) ); diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 9af10966eda4..798f1e6cbeb8 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -100,13 +100,26 @@ pub trait Sized { /// /// All implementations of `Unsize` are provided automatically by the compiler. /// +/// `Unsize` is implemented for: +/// +/// - `[T; N]` is `Unsize<[T]>` +/// - `T` is `Unsize` when `T: Trait` +/// - `Foo<..., T, ...>` is `Unsize>` if: +/// - `T: Unsize` +/// - Foo is a struct +/// - Only the last field of `Foo` has a type involving `T` +/// - `T` is not part of the type of any other fields +/// - `Bar: Unsize>`, if the last field of `Foo` has type `Bar` +/// /// `Unsize` is used along with [`ops::CoerceUnsized`][coerceunsized] to allow /// "user-defined" containers such as [`rc::Rc`][rc] to contain dynamically-sized -/// types. See the [DST coercion RFC][RFC982] for more details. +/// types. See the [DST coercion RFC][RFC982] and [the nomicon entry on coercion][nomicon-coerce] +/// for more details. /// /// [coerceunsized]: ../ops/trait.CoerceUnsized.html /// [rc]: ../../std/rc/struct.Rc.html /// [RFC982]: https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md + #[unstable(feature = "unsize", issue = "27732")] #[lang="unsize"] pub trait Unsize { @@ -234,12 +247,10 @@ pub trait Unsize { /// Generalizing the latter case, any type implementing [`Drop`] can't be `Copy`, because it's /// managing some resource besides its own [`size_of::()`] bytes. /// -/// If you try to implement `Copy` on a struct or enum containing non-`Copy` data, you will get a -/// compile-time error. Specifically, with structs you'll get [E0204] and with enums you'll get -/// [E0205]. +/// If you try to implement `Copy` on a struct or enum containing non-`Copy` data, you will get +/// the error [E0204]. /// /// [E0204]: ../../error-index.html#E0204 -/// [E0205]: ../../error-index.html#E0205 /// /// ## When *should* my type be `Copy`? /// diff --git a/src/libcore/num/i128.rs b/src/libcore/num/i128.rs index 6268271a1dcc..04354e2e33f9 100644 --- a/src/libcore/num/i128.rs +++ b/src/libcore/num/i128.rs @@ -14,4 +14,4 @@ #![unstable(feature = "i128", issue="35118")] -int_module! { i128 } +int_module! { i128, #[unstable(feature = "i128", issue="35118")] } diff --git a/src/libcore/num/int_macros.rs b/src/libcore/num/int_macros.rs index 630fac9d92f0..04311d687ea9 100644 --- a/src/libcore/num/int_macros.rs +++ b/src/libcore/num/int_macros.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,13 +10,14 @@ #![doc(hidden)] -macro_rules! int_module { ($T:ident) => ( - -/// The smallest value that can be represented by this integer type. -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN: $T = $T::min_value(); -/// The largest value that can be represented by this integer type. -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAX: $T = $T::max_value(); - -) } +macro_rules! int_module { + ($T:ident) => (int_module!($T, #[stable(feature = "rust1", since = "1.0.0")]);); + ($T:ident, $($attr: tt)*) => ( + /// The smallest value that can be represented by this integer type. + $($attr)* + pub const MIN: $T = $T::min_value(); + /// The largest value that can be represented by this integer type. + $($attr)* + pub const MAX: $T = $T::max_value(); + ) +} diff --git a/src/libcore/num/u128.rs b/src/libcore/num/u128.rs index 77291f687255..987ac3e00073 100644 --- a/src/libcore/num/u128.rs +++ b/src/libcore/num/u128.rs @@ -13,4 +13,4 @@ //! *[See also the `u128` primitive type](../../std/primitive.u128.html).* #![unstable(feature = "i128", issue="35118")] -uint_module! { u128 } +uint_module! { u128, #[unstable(feature = "i128", issue="35118")] } diff --git a/src/libcore/num/uint_macros.rs b/src/libcore/num/uint_macros.rs index a3a2dc73e9c8..2e59b39278ab 100644 --- a/src/libcore/num/uint_macros.rs +++ b/src/libcore/num/uint_macros.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,13 +10,14 @@ #![doc(hidden)] -macro_rules! uint_module { ($T:ident) => ( - -/// The smallest value that can be represented by this integer type. -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN: $T = $T::min_value(); -/// The largest value that can be represented by this integer type. -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAX: $T = $T::max_value(); - -) } +macro_rules! uint_module { + ($T:ident) => (uint_module!($T, #[stable(feature = "rust1", since = "1.0.0")]);); + ($T:ident, $($attr: tt)*) => ( + /// The smallest value that can be represented by this integer type. + $($attr)* + pub const MIN: $T = $T::min_value(); + /// The largest value that can be represented by this integer type. + $($attr)* + pub const MAX: $T = $T::max_value(); + ) +} diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 40d941a8b27e..94df72f28fa9 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -2710,6 +2710,35 @@ mod impls { /// Trait that indicates that this is a pointer or a wrapper for one, /// where unsizing can be performed on the pointee. +/// +/// See the [DST coercion RfC][dst-coerce] and [the nomicon entry on coercion][nomicon-coerce] +/// for more details. +/// +/// For builtin pointer types, pointers to `T` will coerce to pointers to `U` if `T: Unsize` +/// by converting from a thin pointer to a fat pointer. +/// +/// For custom types, the coercion here works by coercing `Foo` to `Foo` +/// provided an impl of `CoerceUnsized> for Foo` exists. +/// Such an impl can only be written if `Foo` has only a single non-phantomdata +/// field involving `T`. If the type of that field is `Bar`, an implementation +/// of `CoerceUnsized> for Bar` must exist. The coercion will work by +/// by coercing the `Bar` field into `Bar` and filling in the rest of the fields +/// from `Foo` to create a `Foo`. This will effectively drill down to a pointer +/// field and coerce that. +/// +/// Generally, for smart pointers you will implement +/// `CoerceUnsized> for Ptr where T: Unsize, U: ?Sized`, with an +/// optional `?Sized` bound on `T` itself. For wrapper types that directly embed `T` +/// like `Cell` and `RefCell`, you +/// can directly implement `CoerceUnsized> for Wrap where T: CoerceUnsized`. +/// This will let coercions of types like `Cell>` work. +/// +/// [`Unsize`][unsize] is used to mark types which can be coerced to DSTs if behind +/// pointers. It is implemented automatically by the compiler. +/// +/// [dst-coerce]: https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md +/// [unsize]: ../marker/trait.Unsize.html +/// [nomicon-coerce]: ../../nomicon/coercions.html #[unstable(feature = "coerce_unsized", issue = "27732")] #[lang="coerce_unsized"] pub trait CoerceUnsized { diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index e3ca8eca76c5..bf5a59c45e4d 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -74,7 +74,10 @@ pub const fn null_mut() -> *mut T { 0 as *mut T } /// /// # Safety /// -/// This is only unsafe because it accepts a raw pointer. +/// This function copies the memory through the raw pointers passed to it +/// as arguments. +/// +/// Ensure that these pointers are valid before calling `swap`. #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn swap(x: *mut T, y: *mut T) { diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 99c407e5273e..f02df88bb2ef 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -798,6 +798,31 @@ impl Result { Err(e) => e, } } + + /// Unwraps a result, yielding the content of an `Err`. + /// + /// # Panics + /// + /// Panics if the value is an `Ok`, with a panic message including the + /// passed message, and the content of the `Ok`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```{.should_panic} + /// # #![feature(result_expect_err)] + /// let x: Result = Ok(10); + /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err: 10` + /// ``` + #[inline] + #[unstable(feature = "result_expect_err", issue = "39041")] + pub fn expect_err(self, msg: &str) -> E { + match self { + Ok(t) => unwrap_failed(msg, t), + Err(e) => e, + } + } } impl Result { diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index de418b831cc8..49a6b1b5fceb 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1232,10 +1232,15 @@ fn contains_nonascii(x: usize) -> bool { /// invalid sequence. #[inline(always)] fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { - let mut offset = 0; + let mut index = 0; let len = v.len(); - while offset < len { - let old_offset = offset; + + let usize_bytes = mem::size_of::(); + let ascii_block_size = 2 * usize_bytes; + let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 }; + + while index < len { + let old_offset = index; macro_rules! err { () => {{ return Err(Utf8Error { valid_up_to: old_offset @@ -1243,15 +1248,15 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { }}} macro_rules! next { () => {{ - offset += 1; + index += 1; // we needed data, but there was none: error! - if offset >= len { + if index >= len { err!() } - v[offset] + v[index] }}} - let first = v[offset]; + let first = v[index]; if first >= 128 { let w = UTF8_CHAR_WIDTH[first as usize]; let second = next!(); @@ -1294,38 +1299,32 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { } _ => err!() } - offset += 1; + index += 1; } else { // Ascii case, try to skip forward quickly. // When the pointer is aligned, read 2 words of data per iteration // until we find a word containing a non-ascii byte. - let usize_bytes = mem::size_of::(); - let bytes_per_iteration = 2 * usize_bytes; let ptr = v.as_ptr(); - let align = (ptr as usize + offset) & (usize_bytes - 1); + let align = (ptr as usize + index) & (usize_bytes - 1); if align == 0 { - if len >= bytes_per_iteration { - while offset <= len - bytes_per_iteration { - unsafe { - let u = *(ptr.offset(offset as isize) as *const usize); - let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize); - - // break if there is a nonascii byte - let zu = contains_nonascii(u); - let zv = contains_nonascii(v); - if zu || zv { - break; - } + while index < blocks_end { + unsafe { + let block = ptr.offset(index as isize) as *const usize; + // break if there is a nonascii byte + let zu = contains_nonascii(*block); + let zv = contains_nonascii(*block.offset(1)); + if zu | zv { + break; } - offset += bytes_per_iteration; } + index += ascii_block_size; } // step from the point where the wordwise loop stopped - while offset < len && v[offset] < 128 { - offset += 1; + while index < len && v[index] < 128 { + index += 1; } } else { - offset += 1; + index += 1; } } } @@ -1746,13 +1745,31 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) { #[cold] fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { const MAX_DISPLAY_LENGTH: usize = 256; - let (truncated, s) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH); + let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH); let ellipsis = if truncated { "[...]" } else { "" }; + // 1. out of bounds + if begin > s.len() || end > s.len() { + let oob_index = if begin > s.len() { begin } else { end }; + panic!("byte index {} is out of bounds of `{}`{}", oob_index, s_trunc, ellipsis); + } + + // 2. begin <= end assert!(begin <= end, "begin <= end ({} <= {}) when slicing `{}`{}", - begin, end, s, ellipsis); - panic!("index {} and/or {} in `{}`{} do not lie on character boundary", - begin, end, s, ellipsis); + begin, end, s_trunc, ellipsis); + + // 3. character boundary + let index = if !s.is_char_boundary(begin) { begin } else { end }; + // find the character + let mut char_start = index; + while !s.is_char_boundary(char_start) { + char_start -= 1; + } + // `char_start` must be less than len and a char boundary + let ch = s[char_start..].chars().next().unwrap(); + let char_range = char_start .. char_start + ch.len_utf8(); + panic!("byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}", + index, ch, char_range, s_trunc, ellipsis); } #[stable(feature = "core", since = "1.6.0")] diff --git a/src/libcoretest/char.rs b/src/libcoretest/char.rs index b4088ffbf89a..e4012ec91e2b 100644 --- a/src/libcoretest/char.rs +++ b/src/libcoretest/char.rs @@ -75,47 +75,53 @@ fn test_to_digit() { #[test] fn test_to_lowercase() { - fn lower(c: char) -> Vec { - c.to_lowercase().collect() + fn lower(c: char) -> String { + let iter: String = c.to_lowercase().collect(); + let disp: String = c.to_lowercase().to_string(); + assert_eq!(iter, disp); + iter } - assert_eq!(lower('A'), ['a']); - assert_eq!(lower('Ö'), ['ö']); - assert_eq!(lower('ß'), ['ß']); - assert_eq!(lower('Ü'), ['ü']); - assert_eq!(lower('💩'), ['💩']); - assert_eq!(lower('Σ'), ['σ']); - assert_eq!(lower('Τ'), ['τ']); - assert_eq!(lower('Ι'), ['ι']); - assert_eq!(lower('Γ'), ['γ']); - assert_eq!(lower('Μ'), ['μ']); - assert_eq!(lower('Α'), ['α']); - assert_eq!(lower('Σ'), ['σ']); - assert_eq!(lower('Dž'), ['dž']); - assert_eq!(lower('fi'), ['fi']); - assert_eq!(lower('İ'), ['i', '\u{307}']); + assert_eq!(lower('A'), "a"); + assert_eq!(lower('Ö'), "ö"); + assert_eq!(lower('ß'), "ß"); + assert_eq!(lower('Ü'), "ü"); + assert_eq!(lower('💩'), "💩"); + assert_eq!(lower('Σ'), "σ"); + assert_eq!(lower('Τ'), "τ"); + assert_eq!(lower('Ι'), "ι"); + assert_eq!(lower('Γ'), "γ"); + assert_eq!(lower('Μ'), "μ"); + assert_eq!(lower('Α'), "α"); + assert_eq!(lower('Σ'), "σ"); + assert_eq!(lower('Dž'), "dž"); + assert_eq!(lower('fi'), "fi"); + assert_eq!(lower('İ'), "i\u{307}"); } #[test] fn test_to_uppercase() { - fn upper(c: char) -> Vec { - c.to_uppercase().collect() + fn upper(c: char) -> String { + let iter: String = c.to_uppercase().collect(); + let disp: String = c.to_uppercase().to_string(); + assert_eq!(iter, disp); + iter } - assert_eq!(upper('a'), ['A']); - assert_eq!(upper('ö'), ['Ö']); - assert_eq!(upper('ß'), ['S', 'S']); // not ẞ: Latin capital letter sharp s - assert_eq!(upper('ü'), ['Ü']); - assert_eq!(upper('💩'), ['💩']); + assert_eq!(upper('a'), "A"); + assert_eq!(upper('ö'), "Ö"); + assert_eq!(upper('ß'), "SS"); // not ẞ: Latin capital letter sharp s + assert_eq!(upper('ü'), "Ü"); + assert_eq!(upper('💩'), "💩"); - assert_eq!(upper('σ'), ['Σ']); - assert_eq!(upper('τ'), ['Τ']); - assert_eq!(upper('ι'), ['Ι']); - assert_eq!(upper('γ'), ['Γ']); - assert_eq!(upper('μ'), ['Μ']); - assert_eq!(upper('α'), ['Α']); - assert_eq!(upper('ς'), ['Σ']); - assert_eq!(upper('Dž'), ['DŽ']); - assert_eq!(upper('fi'), ['F', 'I']); - assert_eq!(upper('ᾀ'), ['Ἀ', 'Ι']); + assert_eq!(upper('σ'), "Σ"); + assert_eq!(upper('τ'), "Τ"); + assert_eq!(upper('ι'), "Ι"); + assert_eq!(upper('γ'), "Γ"); + assert_eq!(upper('μ'), "Μ"); + assert_eq!(upper('α'), "Α"); + assert_eq!(upper('ς'), "Σ"); + assert_eq!(upper('Dž'), "DŽ"); + assert_eq!(upper('fi'), "FI"); + assert_eq!(upper('ᾀ'), "ἈΙ"); } #[test] @@ -144,107 +150,75 @@ fn test_is_digit() { #[test] fn test_escape_debug() { fn string(c: char) -> String { - c.escape_debug().collect() + let iter: String = c.escape_debug().collect(); + let disp: String = c.escape_debug().to_string(); + assert_eq!(iter, disp); + iter } - let s = string('\n'); - assert_eq!(s, "\\n"); - let s = string('\r'); - assert_eq!(s, "\\r"); - let s = string('\''); - assert_eq!(s, "\\'"); - let s = string('"'); - assert_eq!(s, "\\\""); - let s = string(' '); - assert_eq!(s, " "); - let s = string('a'); - assert_eq!(s, "a"); - let s = string('~'); - assert_eq!(s, "~"); - let s = string('é'); - assert_eq!(s, "é"); - let s = string('文'); - assert_eq!(s, "文"); - let s = string('\x00'); - assert_eq!(s, "\\u{0}"); - let s = string('\x1f'); - assert_eq!(s, "\\u{1f}"); - let s = string('\x7f'); - assert_eq!(s, "\\u{7f}"); - let s = string('\u{80}'); - assert_eq!(s, "\\u{80}"); - let s = string('\u{ff}'); - assert_eq!(s, "\u{ff}"); - let s = string('\u{11b}'); - assert_eq!(s, "\u{11b}"); - let s = string('\u{1d4b6}'); - assert_eq!(s, "\u{1d4b6}"); - let s = string('\u{200b}'); // zero width space - assert_eq!(s, "\\u{200b}"); - let s = string('\u{e000}'); // private use 1 - assert_eq!(s, "\\u{e000}"); - let s = string('\u{100000}'); // private use 2 - assert_eq!(s, "\\u{100000}"); + assert_eq!(string('\n'), "\\n"); + assert_eq!(string('\r'), "\\r"); + assert_eq!(string('\''), "\\'"); + assert_eq!(string('"'), "\\\""); + assert_eq!(string(' '), " "); + assert_eq!(string('a'), "a"); + assert_eq!(string('~'), "~"); + assert_eq!(string('é'), "é"); + assert_eq!(string('文'), "文"); + assert_eq!(string('\x00'), "\\u{0}"); + assert_eq!(string('\x1f'), "\\u{1f}"); + assert_eq!(string('\x7f'), "\\u{7f}"); + assert_eq!(string('\u{80}'), "\\u{80}"); + assert_eq!(string('\u{ff}'), "\u{ff}"); + assert_eq!(string('\u{11b}'), "\u{11b}"); + assert_eq!(string('\u{1d4b6}'), "\u{1d4b6}"); + assert_eq!(string('\u{200b}'),"\\u{200b}"); // zero width space + assert_eq!(string('\u{e000}'), "\\u{e000}"); // private use 1 + assert_eq!(string('\u{100000}'), "\\u{100000}"); // private use 2 } #[test] fn test_escape_default() { fn string(c: char) -> String { - c.escape_default().collect() + let iter: String = c.escape_default().collect(); + let disp: String = c.escape_default().to_string(); + assert_eq!(iter, disp); + iter } - let s = string('\n'); - assert_eq!(s, "\\n"); - let s = string('\r'); - assert_eq!(s, "\\r"); - let s = string('\''); - assert_eq!(s, "\\'"); - let s = string('"'); - assert_eq!(s, "\\\""); - let s = string(' '); - assert_eq!(s, " "); - let s = string('a'); - assert_eq!(s, "a"); - let s = string('~'); - assert_eq!(s, "~"); - let s = string('é'); - assert_eq!(s, "\\u{e9}"); - let s = string('\x00'); - assert_eq!(s, "\\u{0}"); - let s = string('\x1f'); - assert_eq!(s, "\\u{1f}"); - let s = string('\x7f'); - assert_eq!(s, "\\u{7f}"); - let s = string('\u{80}'); - assert_eq!(s, "\\u{80}"); - let s = string('\u{ff}'); - assert_eq!(s, "\\u{ff}"); - let s = string('\u{11b}'); - assert_eq!(s, "\\u{11b}"); - let s = string('\u{1d4b6}'); - assert_eq!(s, "\\u{1d4b6}"); - let s = string('\u{200b}'); // zero width space - assert_eq!(s, "\\u{200b}"); - let s = string('\u{e000}'); // private use 1 - assert_eq!(s, "\\u{e000}"); - let s = string('\u{100000}'); // private use 2 - assert_eq!(s, "\\u{100000}"); + assert_eq!(string('\n'), "\\n"); + assert_eq!(string('\r'), "\\r"); + assert_eq!(string('\''), "\\'"); + assert_eq!(string('"'), "\\\""); + assert_eq!(string(' '), " "); + assert_eq!(string('a'), "a"); + assert_eq!(string('~'), "~"); + assert_eq!(string('é'), "\\u{e9}"); + assert_eq!(string('\x00'), "\\u{0}"); + assert_eq!(string('\x1f'), "\\u{1f}"); + assert_eq!(string('\x7f'), "\\u{7f}"); + assert_eq!(string('\u{80}'), "\\u{80}"); + assert_eq!(string('\u{ff}'), "\\u{ff}"); + assert_eq!(string('\u{11b}'), "\\u{11b}"); + assert_eq!(string('\u{1d4b6}'), "\\u{1d4b6}"); + assert_eq!(string('\u{200b}'), "\\u{200b}"); // zero width space + assert_eq!(string('\u{e000}'), "\\u{e000}"); // private use 1 + assert_eq!(string('\u{100000}'), "\\u{100000}"); // private use 2 } #[test] fn test_escape_unicode() { - fn string(c: char) -> String { c.escape_unicode().collect() } + fn string(c: char) -> String { + let iter: String = c.escape_unicode().collect(); + let disp: String = c.escape_unicode().to_string(); + assert_eq!(iter, disp); + iter + } - let s = string('\x00'); - assert_eq!(s, "\\u{0}"); - let s = string('\n'); - assert_eq!(s, "\\u{a}"); - let s = string(' '); - assert_eq!(s, "\\u{20}"); - let s = string('a'); - assert_eq!(s, "\\u{61}"); - let s = string('\u{11b}'); - assert_eq!(s, "\\u{11b}"); - let s = string('\u{1d4b6}'); - assert_eq!(s, "\\u{1d4b6}"); + assert_eq!(string('\x00'), "\\u{0}"); + assert_eq!(string('\n'), "\\u{a}"); + assert_eq!(string(' '), "\\u{20}"); + assert_eq!(string('a'), "\\u{61}"); + assert_eq!(string('\u{11b}'), "\\u{11b}"); + assert_eq!(string('\u{1d4b6}'), "\\u{1d4b6}"); } #[test] diff --git a/src/libcoretest/iter.rs b/src/libcoretest/iter.rs index 274539dfa669..99d312930533 100644 --- a/src/libcoretest/iter.rs +++ b/src/libcoretest/iter.rs @@ -614,6 +614,14 @@ fn test_iterator_sum() { assert_eq!(v[..0].iter().cloned().sum::(), 0); } +#[test] +fn test_iterator_sum_result() { + let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::>(), Ok(10)); + let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::>(), Err(())); +} + #[test] fn test_iterator_product() { let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; @@ -622,6 +630,14 @@ fn test_iterator_product() { assert_eq!(v[..0].iter().cloned().product::(), 1); } +#[test] +fn test_iterator_product_result() { + let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().product::>(), Ok(24)); + let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().product::>(), Err(())); +} + #[test] fn test_iterator_max() { let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs index d12616a97a64..ee47b510ee07 100644 --- a/src/libcoretest/lib.rs +++ b/src/libcoretest/lib.rs @@ -23,6 +23,7 @@ #![feature(nonzero)] #![feature(rand)] #![feature(raw)] +#![feature(result_expect_err)] #![feature(sip_hash_13)] #![feature(slice_patterns)] #![feature(step_by)] diff --git a/src/libcoretest/result.rs b/src/libcoretest/result.rs index bc2cd8bbfc65..4c5f19dee129 100644 --- a/src/libcoretest/result.rs +++ b/src/libcoretest/result.rs @@ -151,6 +151,19 @@ pub fn test_expect_err() { err.expect("Got expected error"); } + +#[test] +pub fn test_expect_err_err() { + let ok: Result<&'static str, isize> = Err(100); + assert_eq!(ok.expect_err("Unexpected ok"), 100); +} +#[test] +#[should_panic(expected="Got expected ok: \"All good\"")] +pub fn test_expect_err_ok() { + let err: Result<&'static str, isize> = Ok("All good"); + err.expect_err("Got expected ok"); +} + #[test] pub fn test_iter() { let ok: Result = Ok(100); diff --git a/src/libflate/lib.rs b/src/libflate/lib.rs index 3c608ef9c926..8365e9db2a94 100644 --- a/src/libflate/lib.rs +++ b/src/libflate/lib.rs @@ -22,7 +22,7 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(libc)] #![feature(staged_api)] diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index b179a16e55e5..f5a687de64e4 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -23,7 +23,7 @@ html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))))] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(staged_api)] #![feature(unicode)] diff --git a/src/libgetopts/lib.rs b/src/libgetopts/lib.rs index 4d2f1b999a2a..a5cd9fab2cf4 100644 --- a/src/libgetopts/lib.rs +++ b/src/libgetopts/lib.rs @@ -90,6 +90,7 @@ test(attr(deny(warnings))))] #![deny(missing_docs)] +#![deny(warnings)] #![feature(staged_api)] use self::Name::*; @@ -968,7 +969,6 @@ fn test_split_within() { #[cfg(test)] mod tests { use super::*; - use super::Fail::*; use std::result::Result::{Err, Ok}; use std::result; diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index 220051c9d35e..8e587ad211de 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -292,7 +292,7 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(allow(unused_variables), deny(warnings))))] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(str_escape)] diff --git a/src/liblibc b/src/liblibc index 98589876259e..7d57bdcdbb56 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit 98589876259e19f13eab81b033ced95bbb6deca0 +Subproject commit 7d57bdcdbb56540f37afe5a934ce12d33a6ca7fc diff --git a/src/liblog/lib.rs b/src/liblog/lib.rs index 517cd016e8a3..057df647c725 100644 --- a/src/liblog/lib.rs +++ b/src/liblog/lib.rs @@ -168,7 +168,7 @@ html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))))] #![deny(missing_docs)] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(staged_api)] diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index 8f85bfe2c638..348180a48dc3 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -21,7 +21,7 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(staged_api)] diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index b75d9ec6520a..90f4b364482e 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -30,7 +30,7 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(alloc)] #![feature(core_intrinsics)] diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 5ee9fecfb21b..93bc86746256 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -15,22 +15,19 @@ //! Currently the primary use of this crate is to provide the ability to define //! new custom derive modes through `#[proc_macro_derive]`. //! -//! Added recently as part of [RFC 1681] this crate is currently *unstable* and -//! requires the `#![feature(proc_macro_lib)]` directive to use. -//! -//! [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md -//! //! Note that this crate is intentionally very bare-bones currently. The main //! type, `TokenStream`, only supports `fmt::Display` and `FromStr` //! implementations, indicating that it can only go to and come from a string. //! This functionality is intended to be expanded over time as more surface //! area for macro authors is stabilized. +//! +//! See [the book](../../book/procedural-macros.html) for more. #![crate_name = "proc_macro"] -#![unstable(feature = "proc_macro_lib", issue = "27812")] +#![stable(feature = "proc_macro_lib", since = "1.15.0")] #![crate_type = "rlib"] #![crate_type = "dylib"] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![deny(missing_docs)] #![feature(rustc_private)] @@ -55,12 +52,14 @@ use syntax::ptr::P; /// /// The API of this type is intentionally bare-bones, but it'll be expanded over /// time! +#[stable(feature = "proc_macro_lib", since = "1.15.0")] pub struct TokenStream { inner: Vec>, } /// Error returned from `TokenStream::from_str`. #[derive(Debug)] +#[stable(feature = "proc_macro_lib", since = "1.15.0")] pub struct LexError { _inner: (), } @@ -131,6 +130,7 @@ pub mod __internal { } } +#[stable(feature = "proc_macro_lib", since = "1.15.0")] impl FromStr for TokenStream { type Err = LexError; @@ -154,6 +154,7 @@ impl FromStr for TokenStream { } } +#[stable(feature = "proc_macro_lib", since = "1.15.0")] impl fmt::Display for TokenStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for item in self.inner.iter() { diff --git a/src/libproc_macro_plugin/lib.rs b/src/libproc_macro_plugin/lib.rs index c45762bfb6e7..0a7d35258487 100644 --- a/src/libproc_macro_plugin/lib.rs +++ b/src/libproc_macro_plugin/lib.rs @@ -80,7 +80,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(staged_api)] #![feature(rustc_diagnostic_macros)] diff --git a/src/libproc_macro_tokens/lib.rs b/src/libproc_macro_tokens/lib.rs index 3bfa2fbb29fb..0dd9aaab1c63 100644 --- a/src/libproc_macro_tokens/lib.rs +++ b/src/libproc_macro_tokens/lib.rs @@ -52,7 +52,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(staged_api)] #![feature(rustc_private)] diff --git a/src/librand/lib.rs b/src/librand/lib.rs index c31a0ed53207..0cf70880d328 100644 --- a/src/librand/lib.rs +++ b/src/librand/lib.rs @@ -23,7 +23,7 @@ html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))))] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![no_std] #![unstable(feature = "rand", reason = "use `rand` from crates.io", diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 4d66bba9f07e..35afdd75cb8c 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -18,6 +18,7 @@ use hir::{self, PatKind}; struct CFGBuilder<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, + tables: &'a ty::Tables<'tcx>, graph: CFGGraph, fn_exit: CFGIndex, loop_scopes: Vec, @@ -42,10 +43,23 @@ pub fn construct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let fn_exit = graph.add_node(CFGNodeData::Exit); let body_exit; + // Find the function this expression is from. + let mut node_id = body.id; + loop { + let node = tcx.map.get(node_id); + if hir::map::blocks::FnLikeNode::from_node(node).is_some() { + break; + } + let parent = tcx.map.get_parent_node(node_id); + assert!(node_id != parent); + node_id = parent; + } + let mut cfg_builder = CFGBuilder { + tcx: tcx, + tables: tcx.item_tables(tcx.map.local_def_id(node_id)), graph: graph, fn_exit: fn_exit, - tcx: tcx, loop_scopes: Vec::new() }; body_exit = cfg_builder.expr(body, entry); @@ -310,11 +324,11 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { } hir::ExprIndex(ref l, ref r) | - hir::ExprBinary(_, ref l, ref r) if self.tcx.tables().is_method_call(expr.id) => { + hir::ExprBinary(_, ref l, ref r) if self.tables.is_method_call(expr.id) => { self.call(expr, pred, &l, Some(&**r).into_iter()) } - hir::ExprUnary(_, ref e) if self.tcx.tables().is_method_call(expr.id) => { + hir::ExprUnary(_, ref e) if self.tables.is_method_call(expr.id) => { self.call(expr, pred, &e, None::.iter()) } @@ -368,9 +382,9 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { func_or_rcvr: &hir::Expr, args: I) -> CFGIndex { let method_call = ty::MethodCall::expr(call_expr.id); - let fn_ty = match self.tcx.tables().method_map.get(&method_call) { + let fn_ty = match self.tables.method_map.get(&method_call) { Some(method) => method.ty, - None => self.tcx.tables().expr_ty_adjusted(func_or_rcvr) + None => self.tables.expr_ty_adjusted(func_or_rcvr) }; let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index e261c699b6ac..a68876b5ae9c 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -113,6 +113,7 @@ pub enum DepNode { SizedConstraint(D), AssociatedItemDefIds(D), InherentImpls(D), + Tables(D), // The set of impls for a given trait. Ultimately, it would be // nice to get more fine-grained here (e.g., to include a @@ -162,6 +163,7 @@ impl DepNode { ItemSignature, AssociatedItemDefIds, InherentImpls, + Tables, TraitImpls, ReprHints, } @@ -230,6 +232,7 @@ impl DepNode { SizedConstraint(ref d) => op(d).map(SizedConstraint), AssociatedItemDefIds(ref d) => op(d).map(AssociatedItemDefIds), InherentImpls(ref d) => op(d).map(InherentImpls), + Tables(ref d) => op(d).map(Tables), TraitImpls(ref d) => op(d).map(TraitImpls), TraitItems(ref d) => op(d).map(TraitItems), ReprHints(ref d) => op(d).map(ReprHints), diff --git a/src/librustc/hir/map/blocks.rs b/src/librustc/hir/map/blocks.rs index 6e08b52f9a24..91e88e2c73ff 100644 --- a/src/librustc/hir/map/blocks.rs +++ b/src/librustc/hir/map/blocks.rs @@ -45,15 +45,6 @@ pub struct FnLikeNode<'a> { node: map::Node<'a> } /// corresponds to some FnLikeNode. pub trait MaybeFnLike { fn is_fn_like(&self) -> bool; } -/// Components shared by fn-like things (fn items, methods, closures). -pub struct FnParts<'a> { - pub decl: &'a FnDecl, - pub body: ast::BodyId, - pub kind: FnKind<'a>, - pub span: Span, - pub id: NodeId, -} - impl MaybeFnLike for ast::Item { fn is_fn_like(&self) -> bool { match self.node { ast::ItemFn(..) => true, _ => false, } @@ -165,16 +156,6 @@ impl<'a> FnLikeNode<'a> { } } - pub fn to_fn_parts(self) -> FnParts<'a> { - FnParts { - decl: self.decl(), - body: self.body(), - kind: self.kind(), - span: self.span(), - id: self.id(), - } - } - pub fn body(self) -> ast::BodyId { self.handle(|i: ItemFnParts<'a>| i.body, |_, _, _: &'a ast::MethodSig, _, body: ast::BodyId, _, _| body, diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs index a6ffe7cea559..bd0ff695d093 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc/hir/map/collector.rs @@ -23,10 +23,6 @@ pub struct NodeCollector<'ast> { pub(super) map: Vec>, /// The parent of this node pub parent_node: NodeId, - /// If true, completely ignore nested items. We set this when loading - /// HIR from metadata, since in that case we only want the HIR for - /// one specific item (and not the ones nested inside of it). - pub ignore_nested_items: bool } impl<'ast> NodeCollector<'ast> { @@ -35,30 +31,12 @@ impl<'ast> NodeCollector<'ast> { krate: krate, map: vec![], parent_node: CRATE_NODE_ID, - ignore_nested_items: false }; collector.insert_entry(CRATE_NODE_ID, RootCrate); collector } - pub(super) fn extend(krate: &'ast Crate, - parent: &'ast InlinedItem, - parent_node: NodeId, - map: Vec>) - -> NodeCollector<'ast> { - let mut collector = NodeCollector { - krate: krate, - map: map, - parent_node: parent_node, - ignore_nested_items: true - }; - - collector.insert_entry(parent_node, RootInlinedParent(parent)); - - collector - } - fn insert_entry(&mut self, id: NodeId, entry: MapEntry<'ast>) { debug!("ast_map: {:?} => {:?}", id, entry); let len = self.map.len(); @@ -92,27 +70,19 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> { fn visit_nested_item(&mut self, item: ItemId) { debug!("visit_nested_item: {:?}", item); - if !self.ignore_nested_items { - self.visit_item(self.krate.item(item.id)) - } + self.visit_item(self.krate.item(item.id)); } fn visit_nested_trait_item(&mut self, item_id: TraitItemId) { - if !self.ignore_nested_items { - self.visit_trait_item(self.krate.trait_item(item_id)) - } + self.visit_trait_item(self.krate.trait_item(item_id)); } fn visit_nested_impl_item(&mut self, item_id: ImplItemId) { - if !self.ignore_nested_items { - self.visit_impl_item(self.krate.impl_item(item_id)) - } + self.visit_impl_item(self.krate.impl_item(item_id)); } fn visit_nested_body(&mut self, id: BodyId) { - if !self.ignore_nested_items { - self.visit_body(self.krate.body(id)) - } + self.visit_body(self.krate.body(id)); } fn visit_item(&mut self, i: &'ast Item) { diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 27ebeea9ad91..7c0621279fd6 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -25,28 +25,18 @@ use syntax::codemap::Spanned; use syntax_pos::Span; use hir::*; -use hir::intravisit::Visitor; use hir::print::Nested; +use util::nodemap::DefIdMap; use arena::TypedArena; use std::cell::RefCell; use std::io; -use std::mem; pub mod blocks; mod collector; mod def_collector; pub mod definitions; -/// The data we save and restore about an inlined item or method. This is not -/// part of the AST that we parse from a file, but it becomes part of the tree -/// that we trans. -#[derive(Debug)] -struct InlinedItem { - def_id: DefId, - body: Body, -} - #[derive(Copy, Clone, Debug)] pub enum Node<'ast> { NodeItem(&'ast Item), @@ -99,7 +89,6 @@ enum MapEntry<'ast> { /// Roots for node trees. RootCrate, - RootInlinedParent(&'ast InlinedItem) } impl<'ast> Clone for MapEntry<'ast> { @@ -152,8 +141,7 @@ impl<'ast> MapEntry<'ast> { EntryVisibility(id, _) => id, NotPresent | - RootCrate | - RootInlinedParent(_) => return None, + RootCrate => return None, }) } @@ -225,7 +213,7 @@ impl<'ast> MapEntry<'ast> { pub struct Forest { krate: Crate, pub dep_graph: DepGraph, - inlined_items: TypedArena + inlined_bodies: TypedArena } impl Forest { @@ -233,7 +221,7 @@ impl Forest { Forest { krate: krate, dep_graph: dep_graph.clone(), - inlined_items: TypedArena::new() + inlined_bodies: TypedArena::new() } } @@ -263,20 +251,15 @@ pub struct Map<'ast> { /// /// Also, indexing is pretty quick when you've got a vector and /// plain old integers. - map: RefCell>>, + map: Vec>, definitions: Definitions, - /// All NodeIds that are numerically greater or equal to this value come - /// from inlined items. - local_node_id_watermark: NodeId, + /// Bodies inlined from other crates are cached here. + inlined_bodies: RefCell>, } impl<'ast> Map<'ast> { - pub fn is_inlined_node_id(&self, id: NodeId) -> bool { - id >= self.local_node_id_watermark - } - /// Registers a read in the dependency graph of the AST node with /// the given `id`. This needs to be called each time a public /// function returns the HIR for a node -- in other words, when it @@ -289,111 +272,71 @@ impl<'ast> Map<'ast> { } fn dep_node(&self, id0: NodeId) -> DepNode { - let map = self.map.borrow(); let mut id = id0; - if !self.is_inlined_node_id(id) { - let mut last_expr = None; - loop { - let entry = map[id.as_usize()]; - match entry { - EntryItem(..) | - EntryTraitItem(..) | - EntryImplItem(..) => { - if let Some(last_id) = last_expr { - // The body may have a separate dep node - if entry.is_body_owner(last_id) { - let def_id = self.local_def_id(id); - return DepNode::HirBody(def_id); - } - } - return DepNode::Hir(self.local_def_id(id)); - } - - EntryVariant(p, v) => { - id = p; - - if last_expr.is_some() { - if v.node.disr_expr.map(|e| e.node_id) == last_expr { - // The enum parent holds both Hir and HirBody nodes. - let def_id = self.local_def_id(id); - return DepNode::HirBody(def_id); - } + let mut last_expr = None; + loop { + let entry = self.map[id.as_usize()]; + match entry { + EntryItem(..) | + EntryTraitItem(..) | + EntryImplItem(..) => { + if let Some(last_id) = last_expr { + // The body may have a separate dep node + if entry.is_body_owner(last_id) { + let def_id = self.local_def_id(id); + return DepNode::HirBody(def_id); } } - - EntryForeignItem(p, _) | - EntryField(p, _) | - EntryStmt(p, _) | - EntryTy(p, _) | - EntryTraitRef(p, _) | - EntryLocal(p, _) | - EntryPat(p, _) | - EntryBlock(p, _) | - EntryStructCtor(p, _) | - EntryLifetime(p, _) | - EntryTyParam(p, _) | - EntryVisibility(p, _) => - id = p, - - EntryExpr(p, _) => { - last_expr = Some(id); - id = p; - } - - RootCrate => { - return DepNode::Hir(DefId::local(CRATE_DEF_INDEX)); - } - - RootInlinedParent(_) => - bug!("node {} has inlined ancestor but is not inlined", id0), - - NotPresent => - // Some nodes, notably macro definitions, are not - // present in the map for whatever reason, but - // they *do* have def-ids. So if we encounter an - // empty hole, check for that case. - return self.opt_local_def_id(id) - .map(|def_id| DepNode::Hir(def_id)) - .unwrap_or_else(|| { - bug!("Walking parents from `{}` \ - led to `NotPresent` at `{}`", - id0, id) - }), + return DepNode::Hir(self.local_def_id(id)); } - } - } else { - // reading from an inlined def-id is really a read out of - // the metadata from which we loaded the item. - loop { - match map[id.as_usize()] { - EntryItem(p, _) | - EntryForeignItem(p, _) | - EntryTraitItem(p, _) | - EntryImplItem(p, _) | - EntryVariant(p, _) | - EntryField(p, _) | - EntryExpr(p, _) | - EntryStmt(p, _) | - EntryTy(p, _) | - EntryTraitRef(p, _) | - EntryLocal(p, _) | - EntryPat(p, _) | - EntryBlock(p, _) | - EntryStructCtor(p, _) | - EntryLifetime(p, _) | - EntryTyParam(p, _) | - EntryVisibility(p, _) => - id = p, - RootInlinedParent(parent) => - return DepNode::MetaData(parent.def_id), + EntryVariant(p, v) => { + id = p; - RootCrate => - bug!("node {} has crate ancestor but is inlined", id0), - - NotPresent => - bug!("node {} is inlined but not present in map", id0), + if last_expr.is_some() { + if v.node.disr_expr.map(|e| e.node_id) == last_expr { + // The enum parent holds both Hir and HirBody nodes. + let def_id = self.local_def_id(id); + return DepNode::HirBody(def_id); + } + } } + + EntryForeignItem(p, _) | + EntryField(p, _) | + EntryStmt(p, _) | + EntryTy(p, _) | + EntryTraitRef(p, _) | + EntryLocal(p, _) | + EntryPat(p, _) | + EntryBlock(p, _) | + EntryStructCtor(p, _) | + EntryLifetime(p, _) | + EntryTyParam(p, _) | + EntryVisibility(p, _) => + id = p, + + EntryExpr(p, _) => { + last_expr = Some(id); + id = p; + } + + RootCrate => { + return DepNode::Hir(DefId::local(CRATE_DEF_INDEX)); + } + + NotPresent => + // Some nodes, notably macro definitions, are not + // present in the map for whatever reason, but + // they *do* have def-ids. So if we encounter an + // empty hole, check for that case. + return self.opt_local_def_id(id) + .map(|def_id| DepNode::Hir(def_id)) + .unwrap_or_else(|| { + bug!("Walking parents from `{}` \ + led to `NotPresent` at `{}`", + id0, id) + }), } } } @@ -442,11 +385,11 @@ impl<'ast> Map<'ast> { } fn entry_count(&self) -> usize { - self.map.borrow().len() + self.map.len() } fn find_entry(&self, id: NodeId) -> Option> { - self.map.borrow().get(id.as_usize()).cloned() + self.map.get(id.as_usize()).cloned() } pub fn krate(&self) -> &'ast Crate { @@ -483,7 +426,7 @@ impl<'ast> Map<'ast> { /// for embedded constant expressions (e.g. `N` in `[T; N]`). pub fn body_owner(&self, BodyId { node_id }: BodyId) -> NodeId { let parent = self.get_parent_node(node_id); - if self.map.borrow()[parent.as_usize()].is_body_owner(node_id) { + if self.map[parent.as_usize()].is_body_owner(node_id) { parent } else { node_id @@ -644,11 +587,7 @@ impl<'ast> Map<'ast> { } pub fn get_parent_did(&self, id: NodeId) -> DefId { - let parent = self.get_parent(id); - match self.find_entry(parent) { - Some(RootInlinedParent(ii)) => ii.def_id, - _ => self.local_def_id(parent) - } + self.local_def_id(self.get_parent(id)) } pub fn get_foreign_abi(&self, id: NodeId) -> Abi { @@ -660,8 +599,6 @@ impl<'ast> Map<'ast> { _ => None } } - /// Wrong but OK, because the only inlined foreign items are intrinsics. - Some(RootInlinedParent(_)) => Some(Abi::RustIntrinsic), _ => None }; match abi { @@ -737,11 +674,17 @@ impl<'ast> Map<'ast> { } } - pub fn expect_inlined_body(&self, id: NodeId) -> &'ast Body { - match self.find_entry(id) { - Some(RootInlinedParent(inlined_item)) => &inlined_item.body, - _ => bug!("expected inlined item, found {}", self.node_to_string(id)), - } + pub fn get_inlined_body(&self, def_id: DefId) -> Option<&'ast Body> { + self.inlined_bodies.borrow().get(&def_id).map(|&body| { + self.dep_graph.read(DepNode::MetaData(def_id)); + body + }) + } + + pub fn intern_inlined_body(&self, def_id: DefId, body: Body) -> &'ast Body { + let body = self.forest.inlined_bodies.alloc(body); + self.inlined_bodies.borrow_mut().insert(def_id, body); + body } /// Returns the name associated with the given NodeId's AST. @@ -824,7 +767,6 @@ impl<'ast> Map<'ast> { Some(EntryVisibility(_, v)) => bug!("unexpected Visibility {:?}", v), Some(RootCrate) => self.forest.krate.span, - Some(RootInlinedParent(parent)) => parent.body.value.span, Some(NotPresent) | None => { bug!("hir::map::Map::span: id not in map: {:?}", id) } @@ -973,41 +915,15 @@ pub fn map_crate<'ast>(forest: &'ast mut Forest, entries, vector_length, (entries as f64 / vector_length as f64) * 100.); } - let local_node_id_watermark = NodeId::new(map.len()); - Map { forest: forest, dep_graph: forest.dep_graph.clone(), - map: RefCell::new(map), + map: map, definitions: definitions, - local_node_id_watermark: local_node_id_watermark, + inlined_bodies: RefCell::new(DefIdMap()), } } -/// Used for bodies loaded from external crate that are being inlined into this -/// crate. -pub fn map_decoded_body<'ast>(map: &Map<'ast>, - def_id: DefId, - body: Body, - parent_id: NodeId) - -> &'ast Body { - let _ignore = map.forest.dep_graph.in_ignore(); - - let ii = map.forest.inlined_items.alloc(InlinedItem { - def_id: def_id, - body: body - }); - - let mut collector = NodeCollector::extend(map.krate(), - ii, - parent_id, - mem::replace(&mut *map.map.borrow_mut(), vec![])); - collector.visit_body(&ii.body); - *map.map.borrow_mut() = collector.map; - - &ii.body -} - /// Identical to the `PpAnn` implementation for `hir::Crate`, /// except it avoids creating a dependency on the whole crate. impl<'ast> print::PpAnn for Map<'ast> { diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 18fa6836b510..c06c53810d75 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -1210,6 +1210,7 @@ impl<'a> State<'a> { pub fn print_expr(&mut self, expr: &hir::Expr) -> io::Result<()> { self.maybe_print_comment(expr.span.lo)?; + self.print_outer_attributes(&expr.attrs)?; self.ibox(indent_unit)?; self.ann.pre(self, NodeExpr(expr))?; match expr.node { diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index 9ff7dcc7d588..095d2a78a94c 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -113,6 +113,22 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + fn trait_item_scope_tag(item: &hir::TraitItem) -> &'static str { + match item.node { + hir::TraitItemKind::Method(..) => "method body", + hir::TraitItemKind::Const(..) | + hir::TraitItemKind::Type(..) => "associated item" + } + } + + fn impl_item_scope_tag(item: &hir::ImplItem) -> &'static str { + match item.node { + hir::ImplItemKind::Method(..) => "method body", + hir::ImplItemKind::Const(..) | + hir::ImplItemKind::Type(_) => "associated item" + } + } + fn explain_span<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, heading: &str, span: Span) -> (String, Option) { @@ -148,6 +164,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { }, Some(ast_map::NodeStmt(_)) => "statement", Some(ast_map::NodeItem(it)) => item_scope_tag(&it), + Some(ast_map::NodeTraitItem(it)) => trait_item_scope_tag(&it), + Some(ast_map::NodeImplItem(it)) => impl_item_scope_tag(&it), Some(_) | None => { err.span_note(span, &unknown_scope()); return; @@ -186,23 +204,31 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } }; - match self.map.find(fr.scope.node_id(&self.region_maps)) { - Some(ast_map::NodeBlock(ref blk)) => { - let (msg, opt_span) = explain_span(self, "block", blk.span); - (format!("{} {}", prefix, msg), opt_span) + let node = fr.scope.node_id(&self.region_maps); + let unknown; + let tag = match self.map.find(node) { + Some(ast_map::NodeBlock(_)) | + Some(ast_map::NodeExpr(_)) => "body", + Some(ast_map::NodeItem(it)) => item_scope_tag(&it), + Some(ast_map::NodeTraitItem(it)) => trait_item_scope_tag(&it), + Some(ast_map::NodeImplItem(it)) => impl_item_scope_tag(&it), + + // this really should not happen, but it does: + // FIXME(#27942) + Some(_) => { + unknown = format!("unexpected node ({}) for scope {:?}. \ + Please report a bug.", + self.map.node_to_string(node), fr.scope); + &unknown } - Some(ast_map::NodeItem(it)) => { - let tag = item_scope_tag(&it); - let (msg, opt_span) = explain_span(self, tag, it.span); - (format!("{} {}", prefix, msg), opt_span) + None => { + unknown = format!("unknown node for scope {:?}. \ + Please report a bug.", fr.scope); + &unknown } - Some(_) | None => { - // this really should not happen, but it does: - // FIXME(#27942) - (format!("{} unknown free region bounded by scope {:?}", - prefix, fr.scope), None) - } - } + }; + let (msg, opt_span) = explain_span(self, tag, self.map.span(node)); + (format!("{} {}", prefix, msg), opt_span) } ty::ReStatic => ("the static lifetime".to_owned(), None), diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 7b6ed56b779f..b44e1563ee7e 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -27,7 +27,6 @@ use middle::region::CodeExtent; use middle::lang_items; use mir::tcx::LvalueTy; use ty::subst::{Kind, Subst, Substs}; -use ty::adjustment; use ty::{TyVid, IntVid, FloatVid}; use ty::{self, Ty, TyCtxt}; use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; @@ -37,10 +36,11 @@ use traits::{self, ObligationCause, PredicateObligations, Reveal}; use rustc_data_structures::unify::{self, UnificationTable}; use std::cell::{Cell, RefCell, Ref, RefMut}; use std::fmt; +use std::ops::Deref; use syntax::ast; use errors::DiagnosticBuilder; use syntax_pos::{self, Span, DUMMY_SP}; -use util::nodemap::{FxHashMap, FxHashSet, NodeMap}; +use util::nodemap::{FxHashMap, FxHashSet}; use arena::DroplessArena; use self::combine::CombineFields; @@ -76,28 +76,63 @@ pub type Bound = Option; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult = Result; // "fixup result" -/// A version of &ty::Tables which can be global or local. -/// Only the local version supports borrow_mut. +/// A version of &ty::Tables which can be `Missing` (not needed), +/// `InProgress` (during typeck) or `Interned` (result of typeck). +/// Only the `InProgress` version supports `borrow_mut`. #[derive(Copy, Clone)] pub enum InferTables<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { - Global(&'a RefCell>), - Local(&'a RefCell>) + Interned(&'a ty::Tables<'gcx>), + InProgress(&'a RefCell>), + Missing +} + +pub enum InferTablesRef<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { + Interned(&'a ty::Tables<'gcx>), + InProgress(Ref<'a, ty::Tables<'tcx>>) +} + +impl<'a, 'gcx, 'tcx> Deref for InferTablesRef<'a, 'gcx, 'tcx> { + type Target = ty::Tables<'tcx>; + fn deref(&self) -> &Self::Target { + match *self { + InferTablesRef::Interned(tables) => tables, + InferTablesRef::InProgress(ref tables) => tables + } + } } impl<'a, 'gcx, 'tcx> InferTables<'a, 'gcx, 'tcx> { - pub fn borrow(self) -> Ref<'a, ty::Tables<'tcx>> { + pub fn borrow(self) -> InferTablesRef<'a, 'gcx, 'tcx> { match self { - InferTables::Global(tables) => tables.borrow(), - InferTables::Local(tables) => tables.borrow() + InferTables::Interned(tables) => InferTablesRef::Interned(tables), + InferTables::InProgress(tables) => InferTablesRef::InProgress(tables.borrow()), + InferTables::Missing => { + bug!("InferTables: infcx.tables.borrow() with no tables") + } + } + } + + pub fn expect_interned(self) -> &'a ty::Tables<'gcx> { + match self { + InferTables::Interned(tables) => tables, + InferTables::InProgress(_) => { + bug!("InferTables: infcx.tables.expect_interned() during type-checking"); + } + InferTables::Missing => { + bug!("InferTables: infcx.tables.expect_interned() with no tables") + } } } pub fn borrow_mut(self) -> RefMut<'a, ty::Tables<'tcx>> { match self { - InferTables::Global(_) => { + InferTables::Interned(_) => { bug!("InferTables: infcx.tables.borrow_mut() outside of type-checking"); } - InferTables::Local(tables) => tables.borrow_mut() + InferTables::InProgress(tables) => tables.borrow_mut(), + InferTables::Missing => { + bug!("InferTables: infcx.tables.borrow_mut() with no tables") + } } } } @@ -370,27 +405,84 @@ impl fmt::Display for FixupError { } } +pub trait InferEnv<'a, 'tcx> { + fn to_parts(self, tcx: TyCtxt<'a, 'tcx, 'tcx>) + -> (Option<&'a ty::Tables<'tcx>>, + Option>, + Option>); +} + +impl<'a, 'tcx> InferEnv<'a, 'tcx> for () { + fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>) + -> (Option<&'a ty::Tables<'tcx>>, + Option>, + Option>) { + (None, None, None) + } +} + +impl<'a, 'tcx> InferEnv<'a, 'tcx> for ty::ParameterEnvironment<'tcx> { + fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>) + -> (Option<&'a ty::Tables<'tcx>>, + Option>, + Option>) { + (None, None, Some(self)) + } +} + +impl<'a, 'tcx> InferEnv<'a, 'tcx> for (&'a ty::Tables<'tcx>, ty::ParameterEnvironment<'tcx>) { + fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>) + -> (Option<&'a ty::Tables<'tcx>>, + Option>, + Option>) { + (Some(self.0), None, Some(self.1)) + } +} + +impl<'a, 'tcx> InferEnv<'a, 'tcx> for (ty::Tables<'tcx>, ty::ParameterEnvironment<'tcx>) { + fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>) + -> (Option<&'a ty::Tables<'tcx>>, + Option>, + Option>) { + (None, Some(self.0), Some(self.1)) + } +} + +impl<'a, 'tcx> InferEnv<'a, 'tcx> for hir::BodyId { + fn to_parts(self, tcx: TyCtxt<'a, 'tcx, 'tcx>) + -> (Option<&'a ty::Tables<'tcx>>, + Option>, + Option>) { + let item_id = tcx.map.body_owner(self); + (Some(tcx.item_tables(tcx.map.local_def_id(item_id))), + None, + Some(ty::ParameterEnvironment::for_item(tcx, item_id))) + } +} + /// Helper type of a temporary returned by tcx.infer_ctxt(...). /// Necessary because we can't write the following bound: /// F: for<'b, 'tcx> where 'gcx: 'tcx FnOnce(InferCtxt<'b, 'gcx, 'tcx>). pub struct InferCtxtBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { global_tcx: TyCtxt<'a, 'gcx, 'gcx>, arena: DroplessArena, - tables: Option>>, + fresh_tables: Option>>, + tables: Option<&'a ty::Tables<'gcx>>, param_env: Option>, projection_mode: Reveal, } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> { - pub fn infer_ctxt(self, - tables: Option>, - param_env: Option>, - projection_mode: Reveal) - -> InferCtxtBuilder<'a, 'gcx, 'tcx> { + pub fn infer_ctxt>(self, + env: E, + projection_mode: Reveal) + -> InferCtxtBuilder<'a, 'gcx, 'tcx> { + let (tables, fresh_tables, param_env) = env.to_parts(self); InferCtxtBuilder { global_tcx: self, arena: DroplessArena::new(), - tables: tables.map(RefCell::new), + fresh_tables: fresh_tables.map(RefCell::new), + tables: tables, param_env: param_env, projection_mode: projection_mode, } @@ -399,16 +491,17 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> { /// Fake InferCtxt with the global tcx. Used by pre-MIR borrowck /// for MemCategorizationContext/ExprUseVisitor. /// If any inference functionality is used, ICEs will occur. - pub fn borrowck_fake_infer_ctxt(self, param_env: ty::ParameterEnvironment<'gcx>) + pub fn borrowck_fake_infer_ctxt(self, body: hir::BodyId) -> InferCtxt<'a, 'gcx, 'gcx> { + let (tables, _, param_env) = body.to_parts(self); InferCtxt { tcx: self, - tables: InferTables::Global(&self.tables), + tables: InferTables::Interned(tables.unwrap()), type_variables: RefCell::new(type_variable::TypeVariableTable::new()), int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), region_vars: RegionVarBindings::new(self), - parameter_environment: param_env, + parameter_environment: param_env.unwrap(), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), projection_cache: RefCell::new(traits::ProjectionCache::new()), @@ -428,15 +521,14 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { let InferCtxtBuilder { global_tcx, ref arena, - ref tables, + ref fresh_tables, + tables, ref mut param_env, projection_mode, } = *self; - let tables = if let Some(ref tables) = *tables { - InferTables::Local(tables) - } else { - InferTables::Global(&global_tcx.tables) - }; + let tables = tables.map(InferTables::Interned).unwrap_or_else(|| { + fresh_tables.as_ref().map_or(InferTables::Missing, InferTables::InProgress) + }); let param_env = param_env.take().unwrap_or_else(|| { global_tcx.empty_parameter_environment() }); @@ -555,7 +647,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { return value; } - self.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + self.infer_ctxt((), Reveal::All).enter(|infcx| { value.trans_normalize(&infcx) }) } @@ -573,7 +665,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { return value; } - self.infer_ctxt(None, Some(env.clone()), Reveal::All).enter(|infcx| { + self.infer_ctxt(env.clone(), Reveal::All).enter(|infcx| { value.trans_normalize(&infcx) }) } @@ -1490,8 +1582,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Even if the type may have no inference variables, during // type-checking closure types are in local tables only. let local_closures = match self.tables { - InferTables::Local(_) => ty.has_closure_types(), - InferTables::Global(_) => false + InferTables::InProgress(_) => ty.has_closure_types(), + _ => false }; if !local_closures { return ty.moves_by_default(self.tcx.global_tcx(), self.param_env(), span); @@ -1526,15 +1618,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .map(|method| method.def_id) } - pub fn adjustments(&self) -> Ref>> { - fn project_adjustments<'a, 'tcx>(tables: &'a ty::Tables<'tcx>) - -> &'a NodeMap> { - &tables.adjustments - } - - Ref::map(self.tables.borrow(), project_adjustments) - } - pub fn is_method_call(&self, id: ast::NodeId) -> bool { self.tables.borrow().method_map.contains_key(&ty::MethodCall::expr(id)) } @@ -1555,15 +1638,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { def_id: DefId) -> Option { - if def_id.is_local() { - self.tables.borrow().closure_kinds.get(&def_id).cloned() - } else { - // During typeck, ALL closures are local. But afterwards, - // during trans, we see closure ids from other traits. - // That may require loading the closure data out of the - // cstore. - Some(self.tcx.closure_kind(def_id)) + if let InferTables::InProgress(tables) = self.tables { + if let Some(id) = self.tcx.map.as_local_node_id(def_id) { + return tables.borrow().closure_kinds.get(&id).cloned(); + } } + + // During typeck, ALL closures are local. But afterwards, + // during trans, we see closure ids from other traits. + // That may require loading the closure data out of the + // cstore. + Some(self.tcx.closure_kind(def_id)) } pub fn closure_type(&self, @@ -1571,14 +1656,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { substs: ty::ClosureSubsts<'tcx>) -> ty::ClosureTy<'tcx> { - if let InferTables::Local(tables) = self.tables { - if let Some(ty) = tables.borrow().closure_tys.get(&def_id) { - return ty.subst(self.tcx, substs.substs); + if let InferTables::InProgress(tables) = self.tables { + if let Some(id) = self.tcx.map.as_local_node_id(def_id) { + if let Some(ty) = tables.borrow().closure_tys.get(&id) { + return ty.subst(self.tcx, substs.substs); + } } } - let closure_ty = self.tcx.closure_type(def_id, substs); - closure_ty + self.tcx.closure_type(def_id, substs) } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 73ec9d8f8c88..151d7cd17ab1 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -21,7 +21,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(associated_consts)] #![feature(box_patterns)] diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 667c2590fa99..96fb168581b2 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -70,6 +70,12 @@ declare_lint! { "detects unreachable code paths" } +declare_lint! { + pub UNREACHABLE_PATTERNS, + Warn, + "detects unreachable patterns" +} + declare_lint! { pub WARNINGS, Warn, @@ -239,6 +245,7 @@ impl LintPass for HardwiredLints { UNUSED_ASSIGNMENTS, DEAD_CODE, UNREACHABLE_CODE, + UNREACHABLE_PATTERNS, WARNINGS, UNUSED_FEATURES, STABLE_FEATURES, diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 7f908f13dff4..7d85f3607b59 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -27,7 +27,7 @@ use self::TargetLint::*; use dep_graph::DepNode; use middle::privacy::AccessLevels; -use ty::TyCtxt; +use ty::{self, TyCtxt}; use session::{config, early_error, Session}; use lint::{Level, LevelSource, Lint, LintId, LintPass, LintSource}; use lint::{EarlyLintPassObject, LateLintPassObject}; @@ -336,6 +336,9 @@ pub struct LateContext<'a, 'tcx: 'a> { /// Type context we're checking in. pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + /// Side-tables for the body we are in. + pub tables: &'a ty::Tables<'tcx>, + /// The crate being checked. pub krate: &'a hir::Crate, @@ -702,24 +705,6 @@ impl<'a> EarlyContext<'a> { } } -impl<'a, 'tcx> LateContext<'a, 'tcx> { - fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, - krate: &'a hir::Crate, - access_levels: &'a AccessLevels) -> LateContext<'a, 'tcx> { - // We want to own the lint store, so move it out of the session. - let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), - LintStore::new()); - - LateContext { - tcx: tcx, - krate: krate, - access_levels: access_levels, - lints: lint_store, - level_stack: vec![], - } - } -} - impl<'a, 'tcx> LintContext<'tcx> for LateContext<'a, 'tcx> { /// Get the overall compiler `Session` object. fn sess(&self) -> &Session { @@ -796,6 +781,14 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { } } + fn visit_nested_body(&mut self, body: hir::BodyId) { + let old_tables = self.tables; + self.tables = self.tcx.body_tables(body); + let body = self.tcx.map.body(body); + self.visit_body(body); + self.tables = old_tables; + } + fn visit_item(&mut self, it: &'tcx hir::Item) { self.with_lint_attrs(&it.attrs, |cx| { run_lints!(cx, check_item, late_passes, it); @@ -837,10 +830,15 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> { fn visit_fn(&mut self, fk: hir_visit::FnKind<'tcx>, decl: &'tcx hir::FnDecl, body_id: hir::BodyId, span: Span, id: ast::NodeId) { + // Wrap in tables here, not just in visit_nested_body, + // in order for `check_fn` to be able to use them. + let old_tables = self.tables; + self.tables = self.tcx.body_tables(body_id); let body = self.tcx.map.body(body_id); run_lints!(self, check_fn, late_passes, fk, decl, body, span, id); hir_visit::walk_fn(self, fk, decl, body_id, span, id); run_lints!(self, check_fn_post, late_passes, fk, decl, body, span, id); + self.tables = old_tables; } fn visit_variant_data(&mut self, @@ -1209,7 +1207,17 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let _task = tcx.dep_graph.in_task(DepNode::LateLintCheck); let krate = tcx.map.krate(); - let mut cx = LateContext::new(tcx, krate, access_levels); + + // We want to own the lint store, so move it out of the session. + let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), LintStore::new()); + let mut cx = LateContext { + tcx: tcx, + tables: &ty::Tables::empty(), + krate: krate, + access_levels: access_levels, + lints: lint_store, + level_stack: vec![], + }; // Visit the whole crate. cx.with_lint_attrs(&krate.attrs, |cx| { diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 9677082a43a3..f583f601726e 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -15,6 +15,8 @@ use hir::def_id::DefId; use rustc_const_math::*; use self::ConstVal::*; +use std::collections::BTreeMap; + #[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)] pub enum ConstVal { Float(ConstFloat), @@ -22,16 +24,12 @@ pub enum ConstVal { Str(InternedString), ByteStr(Rc>), Bool(bool), - Struct(ast::NodeId), - Tuple(ast::NodeId), Function(DefId), - Array(ast::NodeId, u64), - Repeat(ast::NodeId, u64), + Struct(BTreeMap), + Tuple(Vec), + Array(Vec), + Repeat(Box, u64), Char(char), - /// A value that only occurs in case `eval_const_expr` reported an error. You should never - /// handle this case. Its sole purpose is to allow more errors to be reported instead of - /// causing a fatal error. - Dummy, } impl ConstVal { @@ -48,7 +46,6 @@ impl ConstVal { Array(..) => "array", Repeat(..) => "repeat", Char(..) => "char", - Dummy => "dummy value", } } } diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 7151e5226cab..496a3d4a4984 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -211,6 +211,7 @@ pub trait CrateStore<'tcx> { fn is_foreign_item(&self, did: DefId) -> bool; fn is_dllimport_foreign_item(&self, def: DefId) -> bool; fn is_statically_included_foreign_item(&self, def_id: DefId) -> bool; + fn is_exported_symbol(&self, def_id: DefId) -> bool; // crate metadata fn dylib_dependency_formats(&self, cnum: CrateNum) @@ -258,11 +259,6 @@ pub trait CrateStore<'tcx> { fn get_item_mir<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> Mir<'tcx>; fn is_item_mir_available(&self, def: DefId) -> bool; - /// Take a look if we need to inline or monomorphize this. If so, we - /// will emit code for this item in the local crate, and thus - /// create a translation item for it. - fn can_have_local_instance<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> bool; - // This is basically a 1-based range of ints, which is a little // silly - I may fix that. fn crates(&self) -> Vec; @@ -368,6 +364,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn is_foreign_item(&self, did: DefId) -> bool { bug!("is_foreign_item") } fn is_dllimport_foreign_item(&self, id: DefId) -> bool { false } fn is_statically_included_foreign_item(&self, def_id: DefId) -> bool { false } + fn is_exported_symbol(&self, def_id: DefId) -> bool { false } // crate metadata fn dylib_dependency_formats(&self, cnum: CrateNum) @@ -436,9 +433,6 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn is_item_mir_available(&self, def: DefId) -> bool { bug!("is_item_mir_available") } - fn can_have_local_instance<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> bool { - bug!("can_have_local_instance") - } // This is basically a 1-based range of ints, which is a little // silly - I may fix that. diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 00e21410c707..5af62d0172f5 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -49,6 +49,7 @@ fn should_explore<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, struct MarkSymbolVisitor<'a, 'tcx: 'a> { worklist: Vec, tcx: TyCtxt<'a, 'tcx, 'tcx>, + tables: &'a ty::Tables<'tcx>, live_symbols: Box>, struct_has_extern_repr: bool, ignore_non_const_paths: bool, @@ -57,19 +58,6 @@ struct MarkSymbolVisitor<'a, 'tcx: 'a> { } impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { - fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, - worklist: Vec) -> MarkSymbolVisitor<'a, 'tcx> { - MarkSymbolVisitor { - worklist: worklist, - tcx: tcx, - live_symbols: box FxHashSet(), - struct_has_extern_repr: false, - ignore_non_const_paths: false, - inherited_pub_visibility: false, - ignore_variant_stack: vec![], - } - } - fn check_def_id(&mut self, def_id: DefId) { if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) { if should_explore(self.tcx, node_id) { @@ -109,12 +97,12 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { fn lookup_and_handle_method(&mut self, id: ast::NodeId) { let method_call = ty::MethodCall::expr(id); - let method = self.tcx.tables().method_map[&method_call]; + let method = self.tables.method_map[&method_call]; self.check_def_id(method.def_id); } fn handle_field_access(&mut self, lhs: &hir::Expr, name: ast::Name) { - match self.tcx.tables().expr_ty_adjusted(lhs).sty { + match self.tables.expr_ty_adjusted(lhs).sty { ty::TyAdt(def, _) => { self.insert_def_id(def.struct_variant().field_named(name).did); } @@ -123,7 +111,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } fn handle_tup_field_access(&mut self, lhs: &hir::Expr, idx: usize) { - match self.tcx.tables().expr_ty_adjusted(lhs).sty { + match self.tables.expr_ty_adjusted(lhs).sty { ty::TyAdt(def, _) => { self.insert_def_id(def.struct_variant().fields[idx].did); } @@ -134,7 +122,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { fn handle_field_pattern_match(&mut self, lhs: &hir::Pat, def: Def, pats: &[codemap::Spanned]) { - let variant = match self.tcx.tables().node_id_to_type(lhs.id).sty { + let variant = match self.tables.node_id_to_type(lhs.id).sty { ty::TyAdt(adt, _) => adt.variant_of_def(def), _ => span_bug!(lhs.span, "non-ADT in struct pattern") }; @@ -209,7 +197,15 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.tcx.map) + NestedVisitorMap::None + } + + fn visit_nested_body(&mut self, body: hir::BodyId) { + let old_tables = self.tables; + self.tables = self.tcx.body_tables(body); + let body = self.tcx.map.body(body); + self.visit_body(body); + self.tables = old_tables; } fn visit_variant_data(&mut self, def: &'tcx hir::VariantData, _: ast::Name, @@ -227,7 +223,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr) { match expr.node { hir::ExprPath(ref qpath @ hir::QPath::TypeRelative(..)) => { - let def = self.tcx.tables().qpath_def(qpath, expr.id); + let def = self.tables.qpath_def(qpath, expr.id); self.handle_definition(def); } hir::ExprMethodCall(..) => { @@ -267,7 +263,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { self.handle_field_pattern_match(pat, path.def, fields); } PatKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { - let def = self.tcx.tables().qpath_def(qpath, pat.id); + let def = self.tables.qpath_def(qpath, pat.id); self.handle_definition(def); } _ => () @@ -393,7 +389,16 @@ fn find_live<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, krate: &hir::Crate) -> Box> { let worklist = create_and_seed_worklist(tcx, access_levels, krate); - let mut symbol_visitor = MarkSymbolVisitor::new(tcx, worklist); + let mut symbol_visitor = MarkSymbolVisitor { + worklist: worklist, + tcx: tcx, + tables: &ty::Tables::empty(), + live_symbols: box FxHashSet(), + struct_has_extern_repr: false, + ignore_non_const_paths: false, + inherited_pub_visibility: false, + ignore_variant_stack: vec![], + }; symbol_visitor.mark_live_symbols(); symbol_visitor.live_symbols } diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index 38b0b18b0129..c42e8fcb08ce 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -52,6 +52,7 @@ fn type_is_unsafe_function(ty: Ty) -> bool { struct EffectCheckVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, + tables: &'a ty::Tables<'tcx>, /// Whether we're in an unsafe context. unsafe_context: UnsafeContext, @@ -94,7 +95,15 @@ impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.tcx.map) + NestedVisitorMap::None + } + + fn visit_nested_body(&mut self, body: hir::BodyId) { + let old_tables = self.tables; + self.tables = self.tcx.body_tables(body); + let body = self.tcx.map.body(body); + self.visit_body(body); + self.tables = old_tables; } fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, fn_decl: &'tcx hir::FnDecl, @@ -163,7 +172,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> { match expr.node { hir::ExprMethodCall(..) => { let method_call = MethodCall::expr(expr.id); - let base_type = self.tcx.tables().method_map[&method_call].ty; + let base_type = self.tables.method_map[&method_call].ty; debug!("effect: method call case, base type is {:?}", base_type); if type_is_unsafe_function(base_type) { @@ -172,7 +181,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> { } } hir::ExprCall(ref base, _) => { - let base_type = self.tcx.tables().expr_ty_adjusted(base); + let base_type = self.tables.expr_ty_adjusted(base); debug!("effect: call case, base type is {:?}", base_type); if type_is_unsafe_function(base_type) { @@ -180,7 +189,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> { } } hir::ExprUnary(hir::UnDeref, ref base) => { - let base_type = self.tcx.tables().expr_ty_adjusted(base); + let base_type = self.tables.expr_ty_adjusted(base); debug!("effect: unary case, base type is {:?}", base_type); if let ty::TyRawPtr(_) = base_type.sty { @@ -204,7 +213,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> { } } hir::ExprField(ref base_expr, field) => { - if let ty::TyAdt(adt, ..) = self.tcx.tables().expr_ty_adjusted(base_expr).sty { + if let ty::TyAdt(adt, ..) = self.tables.expr_ty_adjusted(base_expr).sty { if adt.is_union() { self.require_unsafe(field.span, "access to union field"); } @@ -218,7 +227,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> { fn visit_pat(&mut self, pat: &'tcx hir::Pat) { if let PatKind::Struct(_, ref fields, _) = pat.node { - if let ty::TyAdt(adt, ..) = self.tcx.tables().pat_ty(pat).sty { + if let ty::TyAdt(adt, ..) = self.tables.pat_ty(pat).sty { if adt.is_union() { for field in fields { self.require_unsafe(field.span, "matching on union field"); @@ -236,6 +245,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let mut visitor = EffectCheckVisitor { tcx: tcx, + tables: &ty::Tables::empty(), unsafe_context: UnsafeContext::new(SafeContext), }; diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index a3a49c916338..0eacbba3fdd4 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -708,7 +708,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { fn walk_adjustment(&mut self, expr: &hir::Expr) { let infcx = self.mc.infcx; //NOTE(@jroesch): mixed RefCell borrow causes crash - let adj = infcx.adjustments().get(&expr.id).map(|x| x.clone()); + let adj = infcx.tables.borrow().adjustments.get(&expr.id).map(|x| x.clone()); if let Some(adjustment) = adj { match adjustment.kind { adjustment::Adjust::NeverToAny | @@ -989,7 +989,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { PatKind::Struct(ref qpath, ..) => qpath, _ => return }; - let def = tcx.tables().qpath_def(qpath, pat.id); + let def = infcx.tables.borrow().qpath_def(qpath, pat.id); match def { Def::Variant(variant_did) | Def::VariantCtor(variant_did, ..) => { diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index d932061d42d3..d42b866d4720 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -19,7 +19,7 @@ use ty::layout::{LayoutError, Pointer, SizeSkeleton}; use syntax::abi::Abi::RustIntrinsic; use syntax::ast; use syntax_pos::Span; -use hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap}; +use hir::intravisit::{self, Visitor, NestedVisitorMap}; use hir; pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { @@ -33,18 +33,6 @@ struct ItemVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } -impl<'a, 'tcx> ItemVisitor<'a, 'tcx> { - fn visit_const(&mut self, item_id: ast::NodeId, body: hir::BodyId) { - let param_env = ty::ParameterEnvironment::for_item(self.tcx, item_id); - self.tcx.infer_ctxt(None, Some(param_env), Reveal::All).enter(|infcx| { - let mut visitor = ExprVisitor { - infcx: &infcx - }; - visitor.visit_nested_body(body); - }); - } -} - struct ExprVisitor<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx> } @@ -118,64 +106,36 @@ impl<'a, 'gcx, 'tcx> ExprVisitor<'a, 'gcx, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for ItemVisitor<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.tcx.map) + NestedVisitorMap::None } - // const, static and N in [T; N]. - fn visit_body(&mut self, body: &'tcx hir::Body) { - self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + fn visit_nested_body(&mut self, body_id: hir::BodyId) { + let body = self.tcx.map.body(body_id); + self.tcx.infer_ctxt(body_id, Reveal::All).enter(|infcx| { let mut visitor = ExprVisitor { infcx: &infcx }; visitor.visit_body(body); }); - } - - fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem) { - if let hir::TraitItemKind::Const(_, Some(body)) = item.node { - self.visit_const(item.id, body); - } else { - intravisit::walk_trait_item(self, item); - } - } - - fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem) { - if let hir::ImplItemKind::Const(_, body) = item.node { - self.visit_const(item.id, body); - } else { - intravisit::walk_impl_item(self, item); - } - } - - fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx hir::FnDecl, - b: hir::BodyId, s: Span, id: ast::NodeId) { - if let FnKind::Closure(..) = fk { - span_bug!(s, "intrinsicck: closure outside of function") - } - let param_env = ty::ParameterEnvironment::for_item(self.tcx, id); - self.tcx.infer_ctxt(None, Some(param_env), Reveal::All).enter(|infcx| { - let mut visitor = ExprVisitor { - infcx: &infcx - }; - visitor.visit_fn(fk, fd, b, s, id); - }); + self.visit_body(body); } } impl<'a, 'gcx, 'tcx> Visitor<'gcx> for ExprVisitor<'a, 'gcx, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> { - NestedVisitorMap::OnlyBodies(&self.infcx.tcx.map) + NestedVisitorMap::None } fn visit_expr(&mut self, expr: &'gcx hir::Expr) { let def = if let hir::ExprPath(ref qpath) = expr.node { - self.infcx.tcx.tables().qpath_def(qpath, expr.id) + self.infcx.tables.borrow().qpath_def(qpath, expr.id) } else { Def::Err }; match def { Def::Fn(did) if self.def_id_is_transmute(did) => { - let typ = self.infcx.tcx.tables().node_id_to_type(expr.id); + let typ = self.infcx.tables.borrow().node_id_to_type(expr.id); + let typ = self.infcx.tcx.lift_to_global(&typ).unwrap(); match typ.sty { ty::TyFnDef(.., ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => { let from = bare_fn_ty.sig.skip_binder().inputs()[0]; diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 745a94a5ddba..5307b4ec7745 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -351,22 +351,6 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::None - } - - fn visit_local(&mut self, l: &'tcx hir::Local) { - check_local(self, l); - } - fn visit_expr(&mut self, ex: &'tcx Expr) { - check_expr(self, ex); - } - fn visit_arm(&mut self, a: &'tcx hir::Arm) { - check_arm(self, a); - } -} - fn visit_fn<'a, 'tcx: 'a>(ir: &mut IrMaps<'a, 'tcx>, fk: FnKind<'tcx>, decl: &'tcx hir::FnDecl, @@ -394,24 +378,13 @@ fn visit_fn<'a, 'tcx: 'a>(ir: &mut IrMaps<'a, 'tcx>, // and so forth: intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id); - // Special nodes and variables: - // - exit_ln represents the end of the fn, either by return or panic - // - implicit_ret_var is a pseudo-variable that represents - // an implicit return - let specials = Specials { - exit_ln: fn_maps.add_live_node(ExitNode), - fallthrough_ln: fn_maps.add_live_node(ExitNode), - no_ret_var: fn_maps.add_variable(ImplicitRet), - clean_exit_var: fn_maps.add_variable(CleanExit) - }; - // compute liveness - let mut lsets = Liveness::new(&mut fn_maps, specials); + let mut lsets = Liveness::new(&mut fn_maps, body_id); let entry_ln = lsets.compute(&body.value); // check for various error conditions lsets.visit_body(body); - lsets.check_ret(id, sp, fk, entry_ln, body); + lsets.check_ret(id, sp, entry_ln, body); lsets.warn_about_unused_args(body, entry_ln); } @@ -539,6 +512,7 @@ const ACC_USE: u32 = 4; struct Liveness<'a, 'tcx: 'a> { ir: &'a mut IrMaps<'a, 'tcx>, + tables: &'a ty::Tables<'tcx>, s: Specials, successors: Vec, users: Vec, @@ -553,11 +527,26 @@ struct Liveness<'a, 'tcx: 'a> { } impl<'a, 'tcx> Liveness<'a, 'tcx> { - fn new(ir: &'a mut IrMaps<'a, 'tcx>, specials: Specials) -> Liveness<'a, 'tcx> { + fn new(ir: &'a mut IrMaps<'a, 'tcx>, body: hir::BodyId) -> Liveness<'a, 'tcx> { + // Special nodes and variables: + // - exit_ln represents the end of the fn, either by return or panic + // - implicit_ret_var is a pseudo-variable that represents + // an implicit return + let specials = Specials { + exit_ln: ir.add_live_node(ExitNode), + fallthrough_ln: ir.add_live_node(ExitNode), + no_ret_var: ir.add_variable(ImplicitRet), + clean_exit_var: ir.add_variable(CleanExit) + }; + + let tables = ir.tcx.body_tables(body); + let num_live_nodes = ir.num_live_nodes; let num_vars = ir.num_vars; + Liveness { ir: ir, + tables: tables, s: specials, successors: vec![invalid_node(); num_live_nodes], users: vec![invalid_users(); num_live_nodes * num_vars], @@ -1065,7 +1054,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hir::ExprAssignOp(_, ref l, ref r) => { // an overloaded assign op is like a method call - if self.ir.tcx.tables().is_method_call(expr.id) { + if self.tables.is_method_call(expr.id) { let succ = self.propagate_through_expr(&l, succ); self.propagate_through_expr(&r, succ) } else { @@ -1092,8 +1081,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hir::ExprCall(ref f, ref args) => { // FIXME(canndrew): This is_never should really be an is_uninhabited - let diverges = !self.ir.tcx.tables().is_method_call(expr.id) && - self.ir.tcx.tables().expr_ty_adjusted(&f).fn_ret().0.is_never(); + let diverges = !self.tables.is_method_call(expr.id) && + self.tables.expr_ty_adjusted(&f).fn_ret().0.is_never(); let succ = if diverges { self.s.exit_ln } else { @@ -1105,7 +1094,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { hir::ExprMethodCall(.., ref args) => { let method_call = ty::MethodCall::expr(expr.id); - let method_ty = self.ir.tcx.tables().method_map[&method_call].ty; + let method_ty = self.tables.method_map[&method_call].ty; // FIXME(canndrew): This is_never should really be an is_uninhabited let succ = if method_ty.fn_ret().0.is_never() { self.s.exit_ln @@ -1355,6 +1344,22 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // _______________________________________________________________________ // Checking for error conditions +impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + NestedVisitorMap::None + } + + fn visit_local(&mut self, l: &'tcx hir::Local) { + check_local(self, l); + } + fn visit_expr(&mut self, ex: &'tcx Expr) { + check_expr(self, ex); + } + fn visit_arm(&mut self, a: &'tcx hir::Arm) { + check_arm(self, a); + } +} + fn check_local<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, local: &'tcx hir::Local) { match local.init { Some(_) => { @@ -1389,7 +1394,7 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) { } hir::ExprAssignOp(_, ref l, _) => { - if !this.ir.tcx.tables().is_method_call(expr.id) { + if !this.tables.is_method_call(expr.id) { this.check_lvalue(&l); } @@ -1432,15 +1437,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn check_ret(&self, id: NodeId, sp: Span, - fk: FnKind, entry_ln: LiveNode, body: &hir::Body) { - let fn_ty = if let FnKind::Closure(_) = fk { - self.ir.tcx.tables().node_id_to_type(id) - } else { - self.ir.tcx.item_type(self.ir.tcx.map.local_def_id(id)) - }; + let fn_ty = self.ir.tcx.item_type(self.ir.tcx.map.local_def_id(id)); let fn_ret = match fn_ty.sty { ty::TyClosure(closure_def_id, substs) => self.ir.tcx.closure_type(closure_def_id, substs).sig.output(), @@ -1457,8 +1457,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { if !fn_ret.is_never() && self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() { let param_env = ParameterEnvironment::for_item(self.ir.tcx, id); let t_ret_subst = fn_ret.subst(self.ir.tcx, ¶m_env.free_substs); - let is_nil = self.ir.tcx.infer_ctxt(None, Some(param_env), - Reveal::All).enter(|infcx| { + let is_nil = self.ir.tcx.infer_ctxt(param_env, Reveal::All).enter(|infcx| { let cause = traits::ObligationCause::dummy(); traits::fully_normalize(&infcx, cause, &t_ret_subst).unwrap().is_nil() }); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 2d88567b8b87..92e69d7d7295 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -384,7 +384,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { } pub fn cat_expr(&self, expr: &hir::Expr) -> McResult> { - match self.infcx.adjustments().get(&expr.id) { + match self.infcx.tables.borrow().adjustments.get(&expr.id) { None => { // No adjustments. self.cat_expr_unadjusted(expr) diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 765d93742d8f..6eaf3448d028 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -79,6 +79,7 @@ fn method_might_be_inlined<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, struct ReachableContext<'a, 'tcx: 'a> { // The type context. tcx: TyCtxt<'a, 'tcx, 'tcx>, + tables: &'a ty::Tables<'tcx>, // The set of items which must be exported in the linkage sense. reachable_symbols: NodeSet, // A worklist of item IDs. Each item ID in this worklist will be inlined @@ -90,17 +91,25 @@ struct ReachableContext<'a, 'tcx: 'a> { impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.tcx.map) + NestedVisitorMap::None + } + + fn visit_nested_body(&mut self, body: hir::BodyId) { + let old_tables = self.tables; + self.tables = self.tcx.body_tables(body); + let body = self.tcx.map.body(body); + self.visit_body(body); + self.tables = old_tables; } fn visit_expr(&mut self, expr: &'tcx hir::Expr) { let def = match expr.node { hir::ExprPath(ref qpath) => { - Some(self.tcx.tables().qpath_def(qpath, expr.id)) + Some(self.tables.qpath_def(qpath, expr.id)) } hir::ExprMethodCall(..) => { let method_call = ty::MethodCall::expr(expr.id); - let def_id = self.tcx.tables.borrow().method_map[&method_call].def_id; + let def_id = self.tables.method_map[&method_call].def_id; Some(Def::Method(def_id)) } _ => None @@ -135,20 +144,6 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { } impl<'a, 'tcx> ReachableContext<'a, 'tcx> { - // Creates a new reachability computation context. - fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> ReachableContext<'a, 'tcx> { - let any_library = tcx.sess.crate_types.borrow().iter().any(|ty| { - *ty == config::CrateTypeRlib || *ty == config::CrateTypeDylib || - *ty == config::CrateTypeProcMacro - }); - ReachableContext { - tcx: tcx, - reachable_symbols: NodeSet(), - worklist: Vec::new(), - any_library: any_library, - } - } - // Returns true if the given def ID represents a local item that is // eligible for inlining and false otherwise. fn def_id_represents_local_inlined_item(&self, def_id: DefId) -> bool { @@ -369,7 +364,17 @@ pub fn find_reachable<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, -> NodeSet { let _task = tcx.dep_graph.in_task(DepNode::Reachability); - let mut reachable_context = ReachableContext::new(tcx); + let any_library = tcx.sess.crate_types.borrow().iter().any(|ty| { + *ty == config::CrateTypeRlib || *ty == config::CrateTypeDylib || + *ty == config::CrateTypeProcMacro + }); + let mut reachable_context = ReachableContext { + tcx: tcx, + tables: &ty::Tables::empty(), + reachable_symbols: NodeSet(), + worklist: Vec::new(), + any_library: any_library, + }; // Step 1: Seed the worklist with all nodes which were found to be public as // a result of the privacy pass along with all local lang items and impl items. diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index f45e86f2f4b9..e6c9d2c36d01 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -18,7 +18,7 @@ use hir::map as hir_map; use lint; use hir::def::Def; use hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, DefIndex, LOCAL_CRATE}; -use ty::TyCtxt; +use ty::{self, TyCtxt}; use middle::privacy::AccessLevels; use syntax::symbol::Symbol; use syntax_pos::{Span, DUMMY_SP}; @@ -432,6 +432,36 @@ struct Checker<'a, 'tcx: 'a> { } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { + // (See issue #38412) + fn skip_stability_check_due_to_privacy(self, def_id: DefId) -> bool { + let visibility = { + // Check if `def_id` is a trait method. + match self.sess.cstore.associated_item(def_id) { + Some(ty::AssociatedItem { container: ty::TraitContainer(trait_def_id), .. }) => { + // Trait methods do not declare visibility (even + // for visibility info in cstore). Use containing + // trait instead, so methods of pub traits are + // themselves considered pub. + self.sess.cstore.visibility(trait_def_id) + } + _ => { + // Otherwise, cstore info works directly. + self.sess.cstore.visibility(def_id) + } + } + }; + + match visibility { + // must check stability for pub items. + ty::Visibility::Public => false, + + // these are not visible outside crate; therefore + // stability markers are irrelevant, if even present. + ty::Visibility::Restricted(..) | + ty::Visibility::Invisible => true, + } + } + pub fn check_stability(self, def_id: DefId, id: NodeId, span: Span) { if self.sess.codemap().span_allows_unstable(span) { debug!("stability: \ @@ -492,6 +522,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.stability.borrow_mut().used_features.insert(feature.clone(), level.clone()); } + // Issue 38412: private items lack stability markers. + if self.skip_stability_check_due_to_privacy(def_id) { + return + } + match stability { Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => { if !self.stability.borrow().active_features.contains(feature) { diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 3cd358047329..5bae0e347f7c 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -29,7 +29,7 @@ use std::fmt::{self, Debug, Formatter, Write}; use std::{iter, u32}; use std::ops::{Index, IndexMut}; use std::vec::IntoIter; -use syntax::ast::{self, Name}; +use syntax::ast::Name; use syntax_pos::Span; mod cache; @@ -888,6 +888,10 @@ impl<'tcx> Lvalue<'tcx> { self.elem(ProjectionElem::Deref) } + pub fn downcast(self, adt_def: &'tcx AdtDef, variant_index: usize) -> Lvalue<'tcx> { + self.elem(ProjectionElem::Downcast(adt_def, variant_index)) + } + pub fn index(self, index: Operand<'tcx>) -> Lvalue<'tcx> { self.elem(ProjectionElem::Index(index)) } @@ -1267,17 +1271,12 @@ fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { } Bool(b) => write!(fmt, "{:?}", b), Function(def_id) => write!(fmt, "{}", item_path_str(def_id)), - Struct(node_id) | Tuple(node_id) | Array(node_id, _) | Repeat(node_id, _) => - write!(fmt, "{}", node_to_string(node_id)), + Struct(_) | Tuple(_) | Array(_) | Repeat(..) => + bug!("ConstVal `{:?}` should not be in MIR", const_val), Char(c) => write!(fmt, "{:?}", c), - Dummy => bug!(), } } -fn node_to_string(node_id: ast::NodeId) -> String { - ty::tls::with(|tcx| tcx.map.node_to_user_string(node_id)) -} - fn item_path_str(def_id: DefId) -> String { ty::tls::with(|tcx| tcx.item_path_str(def_id)) } diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index b3cb5ace45bc..104c851e057e 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1175,14 +1175,15 @@ pub fn rustc_short_optgroups() -> Vec { assumed.", "[KIND=]NAME"), opt::multi_s("", "crate-type", "Comma separated list of types of crates for the compiler to emit", - "[bin|lib|rlib|dylib|cdylib|staticlib]"), + "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]"), opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"), opt::multi_s("", "emit", "Comma separated list of types of output for \ the compiler to emit", "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info]"), opt::multi_s("", "print", "Comma separated list of compiler information to \ - print on stdout", &print_opts.join("|")), + print on stdout", &format!("[{}]", + &print_opts.join("|"))), opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), opt::opt_s("o", "", "Write output to ", "FILENAME"), diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 5c5bf130c3ba..4893e2409112 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -474,7 +474,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let elaborated_env = unnormalized_env.with_caller_bounds(predicates); - tcx.infer_ctxt(None, Some(elaborated_env), Reveal::NotSpecializable).enter(|infcx| { + tcx.infer_ctxt(elaborated_env, Reveal::NotSpecializable).enter(|infcx| { let predicates = match fully_normalize(&infcx, cause, &infcx.parameter_environment.caller_bounds) { Ok(predicates) => predicates, @@ -576,7 +576,7 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, debug!("normalize_and_test_predicates(predicates={:?})", predicates); - tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let mut selcx = SelectionContext::new(&infcx); let mut fulfill_cx = FulfillmentContext::new(); let cause = ObligationCause::dummy(); diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index 59e3d398b2fd..0fe054b30ba3 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -123,7 +123,7 @@ pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let ancestors = trait_def.ancestors(impl_data.impl_def_id); match ancestors.defs(tcx, name, ty::AssociatedKind::Method).next() { Some(node_item) => { - let substs = tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs); let substs = translate_substs(&infcx, impl_data.impl_def_id, substs, node_item.node); @@ -189,7 +189,7 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, .subst(tcx, &penv.free_substs); // Create a infcx, taking the predicates of impl1 as assumptions: - let result = tcx.infer_ctxt(None, Some(penv), Reveal::ExactMatch).enter(|infcx| { + let result = tcx.infer_ctxt(penv, Reveal::ExactMatch).enter(|infcx| { // Normalize the trait reference. The WF rules ought to ensure // that this always succeeds. let impl1_trait_ref = diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index a41523f2def7..368b1fb4bcbd 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -108,7 +108,7 @@ impl<'a, 'gcx, 'tcx> Children { let possible_sibling = *slot; let tcx = tcx.global_tcx(); - let (le, ge) = tcx.infer_ctxt(None, None, Reveal::ExactMatch).enter(|infcx| { + let (le, ge) = tcx.infer_ctxt((), Reveal::ExactMatch).enter(|infcx| { let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); diff --git a/src/librustc/ty/contents.rs b/src/librustc/ty/contents.rs index 8c3cb7929488..ed5b470849c4 100644 --- a/src/librustc/ty/contents.rs +++ b/src/librustc/ty/contents.rs @@ -235,6 +235,11 @@ impl<'a, 'tcx> ty::TyS<'tcx> { }) }); + if def.is_union() { + // unions don't have destructors regardless of the child types + res = res - TC::NeedsDrop; + } + if def.has_dtor() { res = res | TC::OwnsDtor; } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index f58d7dcb45f3..3df64ebd1581 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -33,6 +33,7 @@ use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, ExistentialPredicate}; use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use ty::TypeVariants::*; use ty::layout::{Layout, TargetDataLayout}; +use ty::inhabitedness::DefIdForest; use ty::maps; use util::common::MemoizationMap; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; @@ -64,6 +65,7 @@ pub struct GlobalArenas<'tcx> { trait_def: TypedArena, adt_def: TypedArena, mir: TypedArena>>, + tables: TypedArena>, } impl<'tcx> GlobalArenas<'tcx> { @@ -74,6 +76,7 @@ impl<'tcx> GlobalArenas<'tcx> { trait_def: TypedArena::new(), adt_def: TypedArena::new(), mir: TypedArena::new(), + tables: TypedArena::new(), } } } @@ -188,6 +191,7 @@ pub struct CommonTypes<'tcx> { pub err: Ty<'tcx>, } +#[derive(RustcEncodable, RustcDecodable)] pub struct Tables<'tcx> { /// Resolved definitions for `::X` associated paths. pub type_relative_path_defs: NodeMap, @@ -210,13 +214,11 @@ pub struct Tables<'tcx> { /// Borrows pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>, - /// Records the type of each closure. The def ID is the ID of the - /// expression defining the closure. - pub closure_tys: DefIdMap>, + /// Records the type of each closure. + pub closure_tys: NodeMap>, - /// Records the type of each closure. The def ID is the ID of the - /// expression defining the closure. - pub closure_kinds: DefIdMap, + /// Records the type of each closure. + pub closure_kinds: NodeMap, /// For each fn, records the "liberated" types of its arguments /// and return type. Liberated means that all bound regions @@ -232,7 +234,7 @@ pub struct Tables<'tcx> { pub fru_field_types: NodeMap>> } -impl<'a, 'gcx, 'tcx> Tables<'tcx> { +impl<'tcx> Tables<'tcx> { pub fn empty() -> Tables<'tcx> { Tables { type_relative_path_defs: NodeMap(), @@ -241,8 +243,8 @@ impl<'a, 'gcx, 'tcx> Tables<'tcx> { adjustments: NodeMap(), method_map: FxHashMap(), upvar_capture_map: FxHashMap(), - closure_tys: DefIdMap(), - closure_kinds: DefIdMap(), + closure_tys: NodeMap(), + closure_kinds: NodeMap(), liberated_fn_sigs: NodeMap(), fru_field_types: NodeMap() } @@ -400,7 +402,7 @@ pub struct GlobalCtxt<'tcx> { free_region_maps: RefCell>, // FIXME: jroesch make this a refcell - pub tables: RefCell>, + pub tables: RefCell>>, /// Maps from a trait item to the trait item "descriptor" pub associated_items: RefCell>>, @@ -459,6 +461,8 @@ pub struct GlobalCtxt<'tcx> { // FIXME dep tracking -- should be harmless enough pub normalized_cache: RefCell, Ty<'tcx>>>, + pub inhabitedness_cache: RefCell, DefIdForest>>, + pub lang_items: middle::lang_items::LanguageItems, /// Maps from def-id of a type or region parameter to its @@ -521,6 +525,14 @@ pub struct GlobalCtxt<'tcx> { /// Caches CoerceUnsized kinds for impls on custom types. pub custom_coerce_unsized_kinds: RefCell>, + /// Records the type of each closure. The def ID is the ID of the + /// expression defining the closure. + pub closure_tys: RefCell>>, + + /// Records the type of each closure. The def ID is the ID of the + /// expression defining the closure. + pub closure_kinds: RefCell>>, + /// Maps a cast expression to its kind. This is keyed on the /// *from* expression of the cast, not the cast itself. pub cast_kinds: RefCell>, @@ -642,6 +654,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.global_arenas.mir.alloc(RefCell::new(mir)) } + pub fn alloc_tables(self, tables: ty::Tables<'gcx>) -> &'gcx ty::Tables<'gcx> { + self.global_arenas.tables.alloc(tables) + } + pub fn alloc_trait_def(self, def: ty::TraitDef) -> &'gcx ty::TraitDef { self.global_arenas.trait_def.alloc(def) } @@ -740,7 +756,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { variance_computed: Cell::new(false), sess: s, trait_map: resolutions.trait_map, - tables: RefCell::new(Tables::empty()), + tables: RefCell::new(DepTrackingMap::new(dep_graph.clone())), impl_trait_refs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), trait_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), adt_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), @@ -760,6 +776,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { associated_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())), ty_param_defs: RefCell::new(NodeMap()), normalized_cache: RefCell::new(FxHashMap()), + inhabitedness_cache: RefCell::new(FxHashMap()), lang_items: lang_items, inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())), used_unsafe: RefCell::new(NodeSet()), @@ -773,6 +790,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { repr_hint_cache: RefCell::new(DepTrackingMap::new(dep_graph.clone())), rvalue_promotable_to_static: RefCell::new(NodeMap()), custom_coerce_unsized_kinds: RefCell::new(DefIdMap()), + closure_tys: RefCell::new(DepTrackingMap::new(dep_graph.clone())), + closure_kinds: RefCell::new(DepTrackingMap::new(dep_graph.clone())), cast_kinds: RefCell::new(NodeMap()), fragment_infos: RefCell::new(DefIdMap()), crate_name: Symbol::intern(crate_name), diff --git a/src/librustc/ty/inhabitedness/def_id_forest.rs b/src/librustc/ty/inhabitedness/def_id_forest.rs new file mode 100644 index 000000000000..16bc65603f13 --- /dev/null +++ b/src/librustc/ty/inhabitedness/def_id_forest.rs @@ -0,0 +1,133 @@ +// Copyright 2012-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. + +use std::mem; +use rustc_data_structures::small_vec::SmallVec; +use syntax::ast::CRATE_NODE_ID; +use ty::context::TyCtxt; +use ty::{DefId, DefIdTree}; + +/// Represents a forest of DefIds closed under the ancestor relation. That is, +/// if a DefId representing a module is contained in the forest then all +/// DefIds defined in that module or submodules are also implicitly contained +/// in the forest. +/// +/// This is used to represent a set of modules in which a type is visibly +/// uninhabited. +#[derive(Clone)] +pub struct DefIdForest { + /// The minimal set of DefIds required to represent the whole set. + /// If A and B are DefIds in the DefIdForest, and A is a desecendant + /// of B, then only B will be in root_ids. + /// We use a SmallVec here because (for its use for cacheing inhabitedness) + /// its rare that this will contain even two ids. + root_ids: SmallVec<[DefId; 1]>, +} + +impl<'a, 'gcx, 'tcx> DefIdForest { + /// Create an empty forest. + pub fn empty() -> DefIdForest { + DefIdForest { + root_ids: SmallVec::new(), + } + } + + /// Create a forest consisting of a single tree representing the entire + /// crate. + #[inline] + pub fn full(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest { + let crate_id = tcx.map.local_def_id(CRATE_NODE_ID); + DefIdForest::from_id(crate_id) + } + + /// Create a forest containing a DefId and all its descendants. + pub fn from_id(id: DefId) -> DefIdForest { + let mut root_ids = SmallVec::new(); + root_ids.push(id); + DefIdForest { + root_ids: root_ids, + } + } + + /// Test whether the forest is empty. + pub fn is_empty(&self) -> bool { + self.root_ids.is_empty() + } + + /// Test whether the forest conains a given DefId. + pub fn contains(&self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + id: DefId) -> bool + { + for root_id in self.root_ids.iter() { + if tcx.is_descendant_of(id, *root_id) { + return true; + } + } + false + } + + /// Calculate the intersection of a collection of forests. + pub fn intersection(tcx: TyCtxt<'a, 'gcx, 'tcx>, + iter: I) -> DefIdForest + where I: IntoIterator + { + let mut ret = DefIdForest::full(tcx); + let mut next_ret = SmallVec::new(); + let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new(); + for next_forest in iter { + for id in ret.root_ids.drain(..) { + if next_forest.contains(tcx, id) { + next_ret.push(id); + } else { + old_ret.push(id); + } + } + ret.root_ids.extend(old_ret.drain(..)); + + for id in next_forest.root_ids { + if ret.contains(tcx, id) { + next_ret.push(id); + } + } + + mem::swap(&mut next_ret, &mut ret.root_ids); + next_ret.drain(..); + } + ret + } + + /// Calculate the union of a collection of forests. + pub fn union(tcx: TyCtxt<'a, 'gcx, 'tcx>, + iter: I) -> DefIdForest + where I: IntoIterator + { + let mut ret = DefIdForest::empty(); + let mut next_ret = SmallVec::new(); + for next_forest in iter { + for id in ret.root_ids.drain(..) { + if !next_forest.contains(tcx, id) { + next_ret.push(id); + } + } + + for id in next_forest.root_ids { + if !next_ret.contains(&id) { + next_ret.push(id); + } + } + + mem::swap(&mut next_ret, &mut ret.root_ids); + next_ret.drain(..); + } + ret + } +} + diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs new file mode 100644 index 000000000000..c5b75839e99b --- /dev/null +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -0,0 +1,199 @@ +// Copyright 2012-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. + +use util::nodemap::FxHashSet; +use ty::context::TyCtxt; +use ty::{AdtDef, VariantDef, FieldDef, TyS}; +use ty::{DefId, Substs}; +use ty::{AdtKind, Visibility}; +use ty::TypeVariants::*; + +pub use self::def_id_forest::DefIdForest; + +mod def_id_forest; + +// The methods in this module calculate DefIdForests of modules in which a +// AdtDef/VariantDef/FieldDef is visibly uninhabited. +// +// # Example +// ```rust +// enum Void {} +// mod a { +// pub mod b { +// pub struct SecretlyUninhabited { +// _priv: !, +// } +// } +// } +// +// mod c { +// pub struct AlsoSecretlyUninhabited { +// _priv: Void, +// } +// mod d { +// } +// } +// +// struct Foo { +// x: a::b::SecretlyUninhabited, +// y: c::AlsoSecretlyUninhabited, +// } +// ``` +// In this code, the type Foo will only be visibly uninhabited inside the +// modules b, c and d. Calling uninhabited_from on Foo or its AdtDef will +// return the forest of modules {b, c->d} (represented in a DefIdForest by the +// set {b, c}) +// +// We need this information for pattern-matching on Foo or types that contain +// Foo. +// +// # Example +// ```rust +// let foo_result: Result = ... ; +// let Ok(t) = foo_result; +// ``` +// This code should only compile in modules where the uninhabitedness of Foo is +// visible. + +impl<'a, 'gcx, 'tcx> AdtDef { + /// Calculate the forest of DefIds from which this adt is visibly uninhabited. + pub fn uninhabited_from( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &'tcx Substs<'tcx>) -> DefIdForest + { + if !visited.insert((self.did, substs)) { + return DefIdForest::empty(); + } + + let ret = DefIdForest::intersection(tcx, self.variants.iter().map(|v| { + v.uninhabited_from(visited, tcx, substs, self.adt_kind()) + })); + visited.remove(&(self.did, substs)); + ret + } +} + +impl<'a, 'gcx, 'tcx> VariantDef { + /// Calculate the forest of DefIds from which this variant is visibly uninhabited. + pub fn uninhabited_from( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &'tcx Substs<'tcx>, + adt_kind: AdtKind) -> DefIdForest + { + match adt_kind { + AdtKind::Union => { + DefIdForest::intersection(tcx, self.fields.iter().map(|f| { + f.uninhabited_from(visited, tcx, substs, false) + })) + }, + AdtKind::Struct => { + DefIdForest::union(tcx, self.fields.iter().map(|f| { + f.uninhabited_from(visited, tcx, substs, false) + })) + }, + AdtKind::Enum => { + DefIdForest::union(tcx, self.fields.iter().map(|f| { + f.uninhabited_from(visited, tcx, substs, true) + })) + }, + } + } +} + +impl<'a, 'gcx, 'tcx> FieldDef { + /// Calculate the forest of DefIds from which this field is visibly uninhabited. + pub fn uninhabited_from( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &'tcx Substs<'tcx>, + is_enum: bool) -> DefIdForest + { + let mut data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(visited, tcx); + // FIXME(canndrew): Currently enum fields are (incorrectly) stored with + // Visibility::Invisible so we need to override self.vis if we're + // dealing with an enum. + if is_enum { + data_uninhabitedness() + } else { + match self.vis { + Visibility::Invisible => DefIdForest::empty(), + Visibility::Restricted(from) => { + let forest = DefIdForest::from_id(from); + let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness())); + DefIdForest::intersection(tcx, iter) + }, + Visibility::Public => data_uninhabitedness(), + } + } + } +} + +impl<'a, 'gcx, 'tcx> TyS<'tcx> { + /// Calculate the forest of DefIds from which this type is visibly uninhabited. + pub fn uninhabited_from( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest + { + match tcx.lift_to_global(&self) { + Some(global_ty) => { + { + let cache = tcx.inhabitedness_cache.borrow(); + if let Some(forest) = cache.get(&global_ty) { + return forest.clone(); + } + } + let forest = global_ty.uninhabited_from_inner(visited, tcx); + let mut cache = tcx.inhabitedness_cache.borrow_mut(); + cache.insert(global_ty, forest.clone()); + forest + }, + None => { + let forest = self.uninhabited_from_inner(visited, tcx); + forest + }, + } + } + + fn uninhabited_from_inner( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest + { + match self.sty { + TyAdt(def, substs) => { + def.uninhabited_from(visited, tcx, substs) + }, + + TyNever => DefIdForest::full(tcx), + TyTuple(ref tys) => { + DefIdForest::union(tcx, tys.iter().map(|ty| { + ty.uninhabited_from(visited, tcx) + })) + }, + TyArray(ty, len) => { + if len == 0 { + DefIdForest::empty() + } else { + ty.uninhabited_from(visited, tcx) + } + } + TyRef(_, ref tm) => tm.ty.uninhabited_from(visited, tcx), + + _ => DefIdForest::empty(), + } + } +} + diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index 42b3544421f8..59d22d270b15 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -46,3 +46,6 @@ dep_map_ty! { ItemVariances: ItemSignature(DefId) -> Rc> } dep_map_ty! { InherentImpls: InherentImpls(DefId) -> Vec } dep_map_ty! { ReprHints: ReprHints(DefId) -> Rc> } dep_map_ty! { Mir: Mir(DefId) -> &'tcx RefCell> } +dep_map_ty! { ClosureKinds: ItemSignature(DefId) -> ty::ClosureKind } +dep_map_ty! { ClosureTypes: ItemSignature(DefId) -> ty::ClosureTy<'tcx> } +dep_map_ty! { Tables: Tables(DefId) -> &'tcx ty::Tables<'tcx> } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 3e33d246b6d1..a88b1e3ece96 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -29,7 +29,7 @@ use ty; use ty::subst::{Subst, Substs}; use ty::walk::TypeWalker; use util::common::MemoizationMap; -use util::nodemap::{NodeSet, NodeMap, FxHashMap, FxHashSet}; +use util::nodemap::{NodeSet, NodeMap, FxHashMap}; use serialize::{self, Encodable, Encoder}; use std::borrow::Cow; @@ -78,6 +78,7 @@ pub mod cast; pub mod error; pub mod fast_reject; pub mod fold; +pub mod inhabitedness; pub mod item_path; pub mod layout; pub mod _match; @@ -226,6 +227,20 @@ pub enum Visibility { pub trait DefIdTree: Copy { fn parent(self, id: DefId) -> Option; + + fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { + if descendant.krate != ancestor.krate { + return false; + } + + while descendant != ancestor { + match self.parent(descendant) { + Some(parent) => descendant = parent, + None => return false, + } + } + true + } } impl<'a, 'gcx, 'tcx> DefIdTree for TyCtxt<'a, 'gcx, 'tcx> { @@ -252,7 +267,7 @@ impl Visibility { } /// Returns true if an item with this visibility is accessible from the given block. - pub fn is_accessible_from(self, mut module: DefId, tree: T) -> bool { + pub fn is_accessible_from(self, module: DefId, tree: T) -> bool { let restriction = match self { // Public items are visible everywhere. Visibility::Public => return true, @@ -263,14 +278,7 @@ impl Visibility { Visibility::Restricted(module) => module, }; - while module != restriction { - match tree.parent(module) { - Some(parent) => module = parent, - None => return false, - } - } - - true + tree.is_descendant_of(module, restriction) } /// Returns true if this visibility is at least as accessible as the given visibility @@ -313,7 +321,7 @@ pub struct MethodCallee<'tcx> { /// needed to add to the side tables. Thus to disambiguate /// we also keep track of whether there's an adjustment in /// our key. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct MethodCall { pub expr_id: NodeId, pub autoderef: u32 @@ -493,7 +501,7 @@ impl Slice { /// Upvars do not get their own node-id. Instead, we use the pair of /// the original var id (that is, the root variable that is referenced /// by the upvar) and the id of the closure expression. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct UpvarId { pub var_id: NodeId, pub closure_expr_id: NodeId, @@ -1406,20 +1414,6 @@ impl<'a, 'gcx, 'tcx> AdtDef { self.flags.set(self.flags.get() | AdtFlags::IS_DTORCK_VALID) } - #[inline] - pub fn is_uninhabited_recurse(&self, - visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - block: Option, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>) -> bool { - if !visited.insert((self.did, substs)) { - return false; - }; - self.variants.iter().all(|v| { - v.is_uninhabited_recurse(visited, block, tcx, substs, self.is_union()) - }) - } - #[inline] pub fn is_struct(&self) -> bool { !self.is_union() && !self.is_enum() @@ -1754,36 +1748,12 @@ impl<'a, 'gcx, 'tcx> VariantDef { pub fn field_named(&self, name: ast::Name) -> &FieldDef { self.find_field_named(name).unwrap() } - - #[inline] - pub fn is_uninhabited_recurse(&self, - visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - block: Option, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - is_union: bool) -> bool { - if is_union { - self.fields.iter().all(|f| f.is_uninhabited_recurse(visited, block, tcx, substs)) - } else { - self.fields.iter().any(|f| f.is_uninhabited_recurse(visited, block, tcx, substs)) - } - } } impl<'a, 'gcx, 'tcx> FieldDef { pub fn ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, subst: &Substs<'tcx>) -> Ty<'tcx> { tcx.item_type(self.did).subst(tcx, subst) } - - #[inline] - pub fn is_uninhabited_recurse(&self, - visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - block: Option, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>) -> bool { - block.map_or(true, |b| tcx.vis_is_accessible_from(self.vis, b)) && - self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx) - } } /// Records the substitutions used to translate the polytype for an @@ -1947,8 +1917,30 @@ impl BorrowKind { } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { - pub fn tables(self) -> Ref<'a, Tables<'gcx>> { - self.tables.borrow() + pub fn body_tables(self, body: hir::BodyId) -> &'gcx Tables<'gcx> { + self.item_tables(self.map.body_owner_def_id(body)) + } + + pub fn item_tables(self, def_id: DefId) -> &'gcx Tables<'gcx> { + self.tables.memoize(def_id, || { + if def_id.is_local() { + // Closures' tables come from their outermost function, + // as they are part of the same "inference environment". + let outer_def_id = self.closure_base_def_id(def_id); + if outer_def_id != def_id { + return self.item_tables(outer_def_id); + } + + bug!("No def'n found for {:?} in tcx.tables", def_id); + } + + // Cross-crate side-tables only exist alongside serialized HIR. + self.sess.cstore.maybe_get_item_body(self.global_tcx(), def_id).map(|_| { + self.tables.borrow()[&def_id] + }).unwrap_or_else(|| { + bug!("tcx.item_tables({:?}): missing from metadata", def_id) + }) + }) } pub fn expr_span(self, id: NodeId) -> Span { @@ -2484,12 +2476,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // If this is a local def-id, it should be inserted into the // tables by typeck; else, it will be retreived from // the external crate metadata. - if let Some(&kind) = self.tables.borrow().closure_kinds.get(&def_id) { + if let Some(&kind) = self.closure_kinds.borrow().get(&def_id) { return kind; } let kind = self.sess.cstore.closure_kind(def_id); - self.tables.borrow_mut().closure_kinds.insert(def_id, kind); + self.closure_kinds.borrow_mut().insert(def_id, kind); kind } @@ -2501,12 +2493,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // If this is a local def-id, it should be inserted into the // tables by typeck; else, it will be retreived from // the external crate metadata. - if let Some(ty) = self.tables.borrow().closure_tys.get(&def_id) { + if let Some(ty) = self.closure_tys.borrow().get(&def_id) { return ty.subst(self, substs.substs); } let ty = self.sess.cstore.closure_ty(self.global_tcx(), def_id); - self.tables.borrow_mut().closure_tys.insert(def_id, ty.clone()); + self.closure_tys.borrow_mut().insert(def_id, ty.clone()); ty.subst(self, substs.substs) } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 638345608c2f..81b0a55841ad 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -22,7 +22,7 @@ use std::fmt; use std::iter; use std::cmp::Ordering; use syntax::abi; -use syntax::ast::{self, Name, NodeId}; +use syntax::ast::{self, Name}; use syntax::symbol::{keywords, InternedString}; use util::nodemap::FxHashSet; @@ -979,29 +979,52 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } - /// Checks whether a type is uninhabited. - /// If `block` is `Some(id)` it also checks that the uninhabited-ness is visible from `id`. - pub fn is_uninhabited(&self, block: Option, cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { + /// Checks whether a type is visibly uninhabited from a particular module. + /// # Example + /// ```rust + /// enum Void {} + /// mod a { + /// pub mod b { + /// pub struct SecretlyUninhabited { + /// _priv: !, + /// } + /// } + /// } + /// + /// mod c { + /// pub struct AlsoSecretlyUninhabited { + /// _priv: Void, + /// } + /// mod d { + /// } + /// } + /// + /// struct Foo { + /// x: a::b::SecretlyUninhabited, + /// y: c::AlsoSecretlyUninhabited, + /// } + /// ``` + /// In this code, the type `Foo` will only be visibly uninhabited inside the + /// modules b, c and d. This effects pattern-matching on `Foo` or types that + /// contain `Foo`. + /// + /// # Example + /// ```rust + /// let foo_result: Result = ... ; + /// let Ok(t) = foo_result; + /// ``` + /// This code should only compile in modules where the uninhabitedness of Foo is + /// visible. + pub fn is_uninhabited_from(&self, module: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { let mut visited = FxHashSet::default(); - self.is_uninhabited_recurse(&mut visited, block, cx) - } + let forest = self.uninhabited_from(&mut visited, tcx); - pub fn is_uninhabited_recurse(&self, - visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - block: Option, - cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { - match self.sty { - TyAdt(def, substs) => { - def.is_uninhabited_recurse(visited, block, cx, substs) - }, - - TyNever => true, - TyTuple(ref tys) => tys.iter().any(|ty| ty.is_uninhabited_recurse(visited, block, cx)), - TyArray(ty, len) => len > 0 && ty.is_uninhabited_recurse(visited, block, cx), - TyRef(_, ref tm) => tm.ty.is_uninhabited_recurse(visited, block, cx), - - _ => false, - } + // To check whether this type is uninhabited at all (not just from the + // given node) you could check whether the forest is empty. + // ``` + // forest.is_empty() + // ``` + forest.contains(tcx, module) } pub fn is_primitive(&self) -> bool { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 0b45ff94a931..65bec9ecdaf9 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -15,7 +15,7 @@ use hir::map::DefPathData; use infer::InferCtxt; use hir::map as ast_map; use traits::{self, Reveal}; -use ty::{self, Ty, AdtKind, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable}; +use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable}; use ty::{Disr, ParameterEnvironment}; use ty::fold::TypeVisitor; use ty::layout::{Layout, LayoutError}; @@ -120,9 +120,8 @@ impl IntTypeExt for attr::IntType { #[derive(Copy, Clone)] -pub enum CopyImplementationError { - InfrigingField(Name), - InfrigingVariant(Name), +pub enum CopyImplementationError<'tcx> { + InfrigingField(&'tcx ty::FieldDef), NotAnAdt, HasDestructor } @@ -145,37 +144,30 @@ pub enum Representability { impl<'tcx> ParameterEnvironment<'tcx> { pub fn can_type_implement_copy<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, self_type: Ty<'tcx>, span: Span) - -> Result<(),CopyImplementationError> { + -> Result<(), CopyImplementationError> { // FIXME: (@jroesch) float this code up - tcx.infer_ctxt(None, Some(self.clone()), Reveal::ExactMatch).enter(|infcx| { - let adt = match self_type.sty { - ty::TyAdt(adt, substs) => match adt.adt_kind() { - AdtKind::Struct | AdtKind::Union => { - for field in adt.all_fields() { - let field_ty = field.ty(tcx, substs); - if infcx.type_moves_by_default(field_ty, span) { - return Err(CopyImplementationError::InfrigingField( - field.name)) - } - } - adt - } - AdtKind::Enum => { - for variant in &adt.variants { - for field in &variant.fields { - let field_ty = field.ty(tcx, substs); - if infcx.type_moves_by_default(field_ty, span) { - return Err(CopyImplementationError::InfrigingVariant( - variant.name)) - } - } - } - adt - } - }, + tcx.infer_ctxt(self.clone(), Reveal::NotSpecializable).enter(|infcx| { + let (adt, substs) = match self_type.sty { + ty::TyAdt(adt, substs) => (adt, substs), _ => return Err(CopyImplementationError::NotAnAdt) }; + let field_implements_copy = |field: &ty::FieldDef| { + let cause = traits::ObligationCause::dummy(); + match traits::fully_normalize(&infcx, cause, &field.ty(tcx, substs)) { + Ok(ty) => !infcx.type_moves_by_default(ty, span), + Err(..) => false + } + }; + + for variant in &adt.variants { + for field in &variant.fields { + if !field_implements_copy(field) { + return Err(CopyImplementationError::InfrigingField(field)); + } + } + } + if adt.has_dtor() { return Err(CopyImplementationError::HasDestructor); } @@ -544,7 +536,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> { } } let result = - tcx.infer_ctxt(None, Some(param_env.clone()), Reveal::ExactMatch) + tcx.infer_ctxt(param_env.clone(), Reveal::ExactMatch) .enter(|infcx| { traits::type_known_to_meet_bound(&infcx, self, def_id, span) }); diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 3dc577b3c647..000e4eb59bf0 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -28,7 +28,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(box_syntax)] #![feature(const_fn)] diff --git a/src/librustc_back/target/redox_base.rs b/src/librustc_back/target/redox_base.rs index 1dffff598096..c5e1e107753f 100644 --- a/src/librustc_back/target/redox_base.rs +++ b/src/librustc_back/target/redox_base.rs @@ -25,14 +25,7 @@ pub fn opts() -> TargetOptions { "-Wl,--as-needed".to_string(), // Always enable NX protection when it is available - "-Wl,-z,noexecstack".to_string(), - - // Static link - "-static".to_string() - ], - late_link_args: vec![ - "-lc".to_string(), - "-lm".to_string() + "-Wl,-z,noexecstack".to_string() ], executables: true, relocation_model: "static".to_string(), @@ -40,7 +33,6 @@ pub fn opts() -> TargetOptions { eliminate_frame_pointer: false, target_family: None, linker_is_gnu: true, - no_default_libraries: true, lib_allocation_crate: "alloc_system".to_string(), exe_allocation_crate: "alloc_system".to_string(), has_elf_tls: true, diff --git a/src/librustc_back/target/s390x_unknown_linux_gnu.rs b/src/librustc_back/target/s390x_unknown_linux_gnu.rs index 6e2dd6cd67c9..671fb4f4319b 100644 --- a/src/librustc_back/target/s390x_unknown_linux_gnu.rs +++ b/src/librustc_back/target/s390x_unknown_linux_gnu.rs @@ -19,6 +19,8 @@ pub fn target() -> TargetResult { // Pass the -vector feature string to LLVM to respect this assumption. base.features = "-vector".to_string(); base.max_atomic_width = Some(64); + // see #36994 + base.exe_allocation_crate = "alloc_system".to_string(); Ok(Target { llvm_target: "s390x-unknown-linux-gnu".to_string(), diff --git a/src/librustc_bitflags/lib.rs b/src/librustc_bitflags/lib.rs index e65d112430a1..edd474b2e9ed 100644 --- a/src/librustc_bitflags/lib.rs +++ b/src/librustc_bitflags/lib.rs @@ -15,7 +15,7 @@ #![crate_type = "rlib"] #![no_std] #![unstable(feature = "rustc_private", issue = "27812")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] //! A typesafe bitmask flag generator. diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index dc2214dd34e7..0605644d497d 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -188,12 +188,10 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, dfcx_loans: &LoanDataFlow<'b, 'tcx>, move_data: &move_data::FlowedMoveData<'c, 'tcx>, all_loans: &[Loan<'tcx>], - fn_id: ast::NodeId, body: &hir::Body) { debug!("check_loans(body id={})", body.value.id); - let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); - let infcx = bccx.tcx.borrowck_fake_infer_ctxt(param_env); + let infcx = bccx.tcx.borrowck_fake_infer_ctxt(body.id()); let mut clcx = CheckLoanCtxt { bccx: bccx, dfcx_loans: dfcx_loans, diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs index 2c277c04a52e..c33ced52e2bd 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs @@ -18,7 +18,7 @@ use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; use rustc::middle::mem_categorization::InteriorOffsetKind as Kind; -use rustc::ty; +use rustc::ty::{self, Ty}; use std::rc::Rc; use syntax::ast; @@ -34,12 +34,10 @@ struct GatherMoveInfo<'tcx> { pub fn gather_decl<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, move_data: &MoveData<'tcx>, - decl_id: ast::NodeId, - _decl_span: Span, - var_id: ast::NodeId) { - let ty = bccx.tcx.tables().node_id_to_type(var_id); - let loan_path = Rc::new(LoanPath::new(LpVar(var_id), ty)); - move_data.add_move(bccx.tcx, loan_path, decl_id, Declared); + var_id: ast::NodeId, + var_ty: Ty<'tcx>) { + let loan_path = Rc::new(LoanPath::new(LpVar(var_id), var_ty)); + move_data.add_move(bccx.tcx, loan_path, var_id, Declared); } pub fn gather_move_from_expr<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index 34f1ad57c621..7101d843b4ce 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -18,6 +18,7 @@ use borrowck::*; use borrowck::move_data::MoveData; +use rustc::infer::InferCtxt; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; @@ -25,7 +26,6 @@ use rustc::middle::region; use rustc::ty::{self, TyCtxt}; use syntax::ast; -use syntax::ast::NodeId; use syntax_pos::Span; use rustc::hir; use rustc::hir::Expr; @@ -40,20 +40,20 @@ mod gather_moves; mod move_error; pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, - fn_id: NodeId, - body: &hir::Body) + body: hir::BodyId) -> (Vec>, move_data::MoveData<'tcx>) { + let infcx = bccx.tcx.borrowck_fake_infer_ctxt(body); let mut glcx = GatherLoanCtxt { bccx: bccx, + infcx: &infcx, all_loans: Vec::new(), - item_ub: bccx.tcx.region_maps.node_extent(body.value.id), + item_ub: bccx.tcx.region_maps.node_extent(body.node_id), move_data: MoveData::new(), move_error_collector: move_error::MoveErrorCollector::new(), }; - let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); - let infcx = bccx.tcx.borrowck_fake_infer_ctxt(param_env); + let body = glcx.bccx.tcx.map.body(body); euv::ExprUseVisitor::new(&mut glcx, &infcx).consume_body(body); glcx.report_potential_errors(); @@ -63,6 +63,7 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, struct GatherLoanCtxt<'a, 'tcx: 'a> { bccx: &'a BorrowckCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'a, 'tcx, 'tcx>, move_data: move_data::MoveData<'tcx>, move_error_collector: move_error::MoveErrorCollector<'tcx>, all_loans: Vec>, @@ -158,8 +159,9 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> { mode); } - fn decl_without_init(&mut self, id: ast::NodeId, span: Span) { - gather_moves::gather_decl(self.bccx, &self.move_data, id, span, id); + fn decl_without_init(&mut self, id: ast::NodeId, _span: Span) { + let ty = self.infcx.tables.borrow().node_id_to_type(id); + gather_moves::gather_decl(self.bccx, &self.move_data, id, ty); } } @@ -516,19 +518,17 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { /// sure the loans being taken are sound. struct StaticInitializerCtxt<'a, 'tcx: 'a> { bccx: &'a BorrowckCtxt<'a, 'tcx>, - item_id: ast::NodeId + body_id: hir::BodyId, } impl<'a, 'tcx> Visitor<'tcx> for StaticInitializerCtxt<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.bccx.tcx.map) + NestedVisitorMap::None } fn visit_expr(&mut self, ex: &'tcx Expr) { if let hir::ExprAddrOf(mutbl, ref base) = ex.node { - let param_env = ty::ParameterEnvironment::for_item(self.bccx.tcx, - self.item_id); - let infcx = self.bccx.tcx.borrowck_fake_infer_ctxt(param_env); + let infcx = self.bccx.tcx.borrowck_fake_infer_ctxt(self.body_id); let mc = mc::MemCategorizationContext::new(&infcx); let base_cmt = mc.cat_expr(&base).unwrap(); let borrow_kind = ty::BorrowKind::from_mutbl(mutbl); @@ -545,16 +545,14 @@ impl<'a, 'tcx> Visitor<'tcx> for StaticInitializerCtxt<'a, 'tcx> { } } -pub fn gather_loans_in_static_initializer<'a, 'tcx>(bccx: &mut BorrowckCtxt<'a, 'tcx>, - item_id: ast::NodeId, - body: hir::BodyId) { - +pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, body: hir::BodyId) { debug!("gather_loans_in_static_initializer(expr={:?})", body); let mut sicx = StaticInitializerCtxt { bccx: bccx, - item_id: item_id + body_id: body }; - sicx.visit_nested_body(body); + let body = sicx.bccx.tcx.map.body(body); + sicx.visit_body(body); } diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 1ba313015d59..c0e038d183da 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -24,7 +24,7 @@ use self::InteriorKind::*; use rustc::dep_graph::DepNode; use rustc::hir::map as hir_map; -use rustc::hir::map::blocks::{FnParts, FnLikeNode}; +use rustc::hir::map::blocks::FnLikeNode; use rustc::cfg; use rustc::middle::dataflow::DataFlowContext; use rustc::middle::dataflow::BitwiseOperator; @@ -89,14 +89,14 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowckCtxt<'a, 'tcx> { fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) { if let hir::TraitItemKind::Const(_, Some(expr)) = ti.node { - gather_loans::gather_loans_in_static_initializer(self, ti.id, expr); + gather_loans::gather_loans_in_static_initializer(self, expr); } intravisit::walk_trait_item(self, ti); } fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) { if let hir::ImplItemKind::Const(_, expr) = ii.node { - gather_loans::gather_loans_in_static_initializer(self, ii.id, expr); + gather_loans::gather_loans_in_static_initializer(self, expr); } intravisit::walk_impl_item(self, ii); } @@ -143,7 +143,7 @@ fn borrowck_item<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, item: &'tcx hir::I match item.node { hir::ItemStatic(.., ex) | hir::ItemConst(_, ex) => { - gather_loans::gather_loans_in_static_initializer(this, item.id, ex); + gather_loans::gather_loans_in_static_initializer(this, ex); } _ => { } } @@ -179,7 +179,7 @@ fn borrowck_fn<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, let AnalysisData { all_loans, loans: loan_dfcx, move_data: flowed_moves } = - build_borrowck_dataflow_data(this, &cfg, body, id); + build_borrowck_dataflow_data(this, &cfg, body_id); move_data::fragments::instrument_move_fragments(&flowed_moves.move_data, this.tcx, @@ -189,31 +189,26 @@ fn borrowck_fn<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, &flowed_moves.move_data, id); - check_loans::check_loans(this, - &loan_dfcx, - &flowed_moves, - &all_loans[..], - id, - body); + check_loans::check_loans(this, &loan_dfcx, &flowed_moves, &all_loans[..], body); intravisit::walk_fn(this, fk, decl, body_id, sp, id); } fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, cfg: &cfg::CFG, - body: &'tcx hir::Body, - id: ast::NodeId) + body_id: hir::BodyId) -> AnalysisData<'a, 'tcx> { // Check the body of fn items. let tcx = this.tcx; + let body = tcx.map.body(body_id); let id_range = { let mut visitor = intravisit::IdRangeComputingVisitor::new(&tcx.map); visitor.visit_body(body); visitor.result() }; let (all_loans, move_data) = - gather_loans::gather_loans_in_fn(this, id, body); + gather_loans::gather_loans_in_fn(this, body_id); let mut loan_dfcx = DataFlowContext::new(this.tcx, @@ -246,7 +241,7 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer. pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - fn_parts: FnParts<'tcx>, + body: hir::BodyId, cfg: &cfg::CFG) -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) { @@ -262,13 +257,7 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>( } }; - let body = tcx.map.body(fn_parts.body); - - let dataflow_data = build_borrowck_dataflow_data(&mut bccx, - cfg, - body, - fn_parts.id); - + let dataflow_data = build_borrowck_dataflow_data(&mut bccx, cfg, body); (bccx, dataflow_data) } diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index 1ff232da427f..d3b22884a3d8 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -15,7 +15,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![allow(non_camel_case_types)] diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 6d04975f533d..f4b3646fce02 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -17,14 +17,14 @@ use eval::{compare_const_vals}; use rustc_const_math::ConstInt; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::Idx; use pattern::{FieldPattern, Pattern, PatternKind}; use pattern::{PatternFoldable, PatternFolder}; use rustc::hir::def_id::DefId; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable}; use rustc::mir::Field; use rustc::util::common::ErrorReported; @@ -144,9 +144,13 @@ impl<'a, 'tcx> FromIterator>> for Matrix<'a, 'tcx> { //NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv pub struct MatchCheckCtxt<'a, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'tcx, 'tcx>, - /// A wild pattern with an error type - it exists to avoid having to normalize - /// associated types to get field types. - pub wild_pattern: &'a Pattern<'tcx>, + /// The module in which the match occurs. This is necessary for + /// checking inhabited-ness of types because whether a type is (visibly) + /// inhabited can depend on whether it was defined in the current module or + /// not. eg. `struct Foo { _private: ! }` cannot be seen to be empty + /// outside it's module and should not be matchable with an empty match + /// statement. + pub module: DefId, pub pattern_arena: &'a TypedArena>, pub byte_array_map: FxHashMap<*const Pattern<'tcx>, Vec<&'a Pattern<'tcx>>>, } @@ -154,27 +158,24 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> { impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { pub fn create_and_enter( tcx: TyCtxt<'a, 'tcx, 'tcx>, + module: DefId, f: F) -> R where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R { - let wild_pattern = Pattern { - ty: tcx.types.err, - span: DUMMY_SP, - kind: box PatternKind::Wild - }; - let pattern_arena = TypedArena::new(); f(MatchCheckCtxt { tcx: tcx, - wild_pattern: &wild_pattern, + module: module, pattern_arena: &pattern_arena, byte_array_map: FxHashMap(), }) } // convert a byte-string pattern to a list of u8 patterns. - fn lower_byte_str_pattern(&mut self, pat: &'a Pattern<'tcx>) -> Vec<&'a Pattern<'tcx>> { + fn lower_byte_str_pattern<'p>(&mut self, pat: &'p Pattern<'tcx>) -> Vec<&'p Pattern<'tcx>> + where 'a: 'p + { let pattern_arena = &*self.pattern_arena; let tcx = self.tcx; self.byte_array_map.entry(pat).or_insert_with(|| { @@ -269,8 +270,14 @@ impl<'tcx> Witness<'tcx> { ty: Ty<'tcx>) -> Self { - let arity = constructor_arity(cx, ctor, ty); - self.0.extend(repeat(cx.wild_pattern).take(arity).cloned()); + let sub_pattern_tys = constructor_sub_pattern_tys(cx, ctor, ty); + self.0.extend(sub_pattern_tys.into_iter().map(|ty| { + Pattern { + ty: ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + } + })); self.apply_constructor(cx, ctor, ty) } @@ -310,10 +317,11 @@ impl<'tcx> Witness<'tcx> { } }).collect(); - if let ty::TyAdt(adt, _) = ty.sty { + if let ty::TyAdt(adt, substs) = ty.sty { if adt.variants.len() > 1 { PatternKind::Variant { adt_def: adt, + substs: substs, variant_index: ctor.variant_index_for_adt(adt), subpatterns: pats } @@ -356,51 +364,65 @@ impl<'tcx> Witness<'tcx> { } } -/// Return the set of constructors from the same type as the first column of `matrix`, -/// that are matched only by wildcard patterns from that first column. -/// -/// Therefore, if there is some pattern that is unmatched by `matrix`, it will -/// still be unmatched if the first constructor is replaced by any of the constructors -/// in the return value. -fn missing_constructors(cx: &mut MatchCheckCtxt, - matrix: &Matrix, - pcx: PatternContext) -> Vec { - let used_constructors: Vec = - matrix.0.iter() - .flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![])) - .collect(); - debug!("used_constructors = {:?}", used_constructors); - all_constructors(cx, pcx).into_iter() - .filter(|c| !used_constructors.contains(c)) - .collect() -} - /// This determines the set of all possible constructors of a pattern matching /// values of type `left_ty`. For vectors, this would normally be an infinite set +/// but is instead bounded by the maximum fixed length of slice patterns in +/// the column of patterns being analyzed. /// /// This intentionally does not list ConstantValue specializations for /// non-booleans, because we currently assume that there is always a /// "non-standard constant" that matches. See issue #12483. /// -/// but is instead bounded by the maximum fixed length of slice patterns in -/// the column of patterns being analyzed. -fn all_constructors(_cx: &mut MatchCheckCtxt, pcx: PatternContext) -> Vec { +/// We make sure to omit constructors that are statically impossible. eg for +/// Option we do not include Some(_) in the returned list of constructors. +fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, + pcx: PatternContext<'tcx>) -> Vec +{ + debug!("all_constructors({:?})", pcx.ty); match pcx.ty.sty { ty::TyBool => [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), - ty::TySlice(_) => - (0..pcx.max_slice_length+1).map(|length| Slice(length)).collect(), - ty::TyArray(_, length) => vec![Slice(length)], - ty::TyAdt(def, _) if def.is_enum() && def.variants.len() > 1 => - def.variants.iter().map(|v| Variant(v.did)).collect(), - _ => vec![Single] + ty::TySlice(ref sub_ty) => { + if sub_ty.is_uninhabited_from(cx.module, cx.tcx) { + vec![Slice(0)] + } else { + (0..pcx.max_slice_length+1).map(|length| Slice(length)).collect() + } + } + ty::TyArray(ref sub_ty, length) => { + if length == 0 || !sub_ty.is_uninhabited_from(cx.module, cx.tcx) { + vec![Slice(length)] + } else { + vec![] + } + } + ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => { + def.variants.iter().filter_map(|v| { + let mut visited = FxHashSet::default(); + let forest = v.uninhabited_from(&mut visited, + cx.tcx, substs, + AdtKind::Enum); + if forest.contains(cx.tcx, cx.module) { + None + } else { + Some(Variant(v.did)) + } + }).collect() + } + _ => { + if pcx.ty.is_uninhabited_from(cx.module, cx.tcx) { + vec![] + } else { + vec![Single] + } + } } } -fn max_slice_length<'a, 'tcx, I>( +fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( _cx: &mut MatchCheckCtxt<'a, 'tcx>, patterns: I) -> usize - where I: Iterator> + where I: Iterator> { // The exhaustiveness-checking paper does not include any details on // checking variable-length slice patterns. However, they are matched @@ -491,6 +513,12 @@ fn max_slice_length<'a, 'tcx, I>( } /// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html +/// The algorithm from the paper has been modified to correctly handle empty +/// types. The changes are: +/// (0) We don't exit early if the pattern matrix has zero rows. We just +/// continue to recurse over columns. +/// (1) all_constructors will only return constructors that are statically +/// possible. eg. it will only return Ok for Result /// /// Whether a vector `v` of patterns is 'useful' in relation to a set of such /// vectors `m` is defined as there being a set of inputs that will match `v` @@ -500,29 +528,30 @@ fn max_slice_length<'a, 'tcx, I>( /// relation to preceding patterns, it is not reachable) and exhaustiveness /// checking (if a wildcard pattern is useful in relation to a matrix, the /// matrix isn't exhaustive). -/// -/// Note: is_useful doesn't work on empty types, as the paper notes. -/// So it assumes that v is non-empty. -pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, - matrix: &Matrix<'a, 'tcx>, - v: &[&'a Pattern<'tcx>], +pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, + matrix: &Matrix<'p, 'tcx>, + v: &[&'p Pattern<'tcx>], witness: WitnessPreference) -> Usefulness<'tcx> { let &Matrix(ref rows) = matrix; debug!("is_useful({:?}, {:?})", matrix, v); - if rows.is_empty() { - return match witness { - ConstructWitness => UsefulWithWitness(vec![Witness( - repeat(cx.wild_pattern).take(v.len()).cloned().collect() - )]), - LeaveOutWitness => Useful - }; - } - if rows[0].is_empty() { - return NotUseful; - } - let &Matrix(ref rows) = matrix; + // The base case. We are pattern-matching on () and the return value is + // based on whether our matrix has a row or not. + // NOTE: This could potentially be optimized by checking rows.is_empty() + // first and then, if v is non-empty, the return value is based on whether + // the type of the tuple we're checking is inhabited or not. + if v.is_empty() { + return if rows.is_empty() { + match witness { + ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), + LeaveOutWitness => Useful, + } + } else { + NotUseful + } + }; + assert!(rows.iter().all(|r| r.len() == v.len())); @@ -541,10 +570,28 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ).find(|result| result.is_useful()).unwrap_or(NotUseful) } else { debug!("is_useful - expanding wildcard"); - let constructors = missing_constructors(cx, matrix, pcx); - debug!("is_useful - missing_constructors = {:?}", constructors); - if constructors.is_empty() { - all_constructors(cx, pcx).into_iter().map(|c| { + + let used_ctors: Vec = rows.iter().flat_map(|row| { + pat_constructors(cx, row[0], pcx).unwrap_or(vec![]) + }).collect(); + debug!("used_ctors = {:?}", used_ctors); + let all_ctors = all_constructors(cx, pcx); + debug!("all_ctors = {:?}", all_ctors); + let missing_ctors: Vec = all_ctors.iter().filter(|c| { + !used_ctors.contains(*c) + }).cloned().collect(); + debug!("missing_ctors = {:?}", missing_ctors); + + // `missing_ctors` is the set of constructors from the same type as the + // first column of `matrix` that are matched only by wildcard patterns + // from the first column. + // + // Therefore, if there is some pattern that is unmatched by `matrix`, + // it will still be unmatched if the first constructor is replaced by + // any of the constructors in `missing_ctors` + + if missing_ctors.is_empty() { + all_ctors.into_iter().map(|c| { is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) }).find(|result| result.is_useful()).unwrap_or(NotUseful) } else { @@ -558,11 +605,25 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, match is_useful(cx, &matrix, &v[1..], witness) { UsefulWithWitness(pats) => { let cx = &*cx; - UsefulWithWitness(pats.into_iter().flat_map(|witness| { - constructors.iter().map(move |ctor| { - witness.clone().push_wild_constructor(cx, ctor, pcx.ty) - }) - }).collect()) + let new_witnesses = if used_ctors.is_empty() { + // All constructors are unused. Add wild patterns + // rather than each individual constructor + pats.into_iter().map(|mut witness| { + witness.0.push(Pattern { + ty: pcx.ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + }); + witness + }).collect() + } else { + pats.into_iter().flat_map(|witness| { + missing_ctors.iter().map(move |ctor| { + witness.clone().push_wild_constructor(cx, ctor, pcx.ty) + }) + }).collect() + }; + UsefulWithWitness(new_witnesses) } result => result } @@ -570,19 +631,27 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } } -fn is_useful_specialized<'a, 'tcx>( +fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - &Matrix(ref m): &Matrix<'a, 'tcx>, - v: &[&'a Pattern<'tcx>], + &Matrix(ref m): &Matrix<'p, 'tcx>, + v: &[&'p Pattern<'tcx>], ctor: Constructor, lty: Ty<'tcx>, witness: WitnessPreference) -> Usefulness<'tcx> { - let arity = constructor_arity(cx, &ctor, lty); + let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty); + let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| { + Pattern { + ty: ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + } + }).collect(); + let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect(); let matrix = Matrix(m.iter().flat_map(|r| { - specialize(cx, &r[..], &ctor, 0, arity) + specialize(cx, &r[..], &ctor, &wild_patterns) }).collect()); - match specialize(cx, v, &ctor, 0, arity) { + match specialize(cx, v, &ctor, &wild_patterns) { Some(v) => match is_useful(cx, &matrix, &v[..], witness) { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses.into_iter() @@ -657,6 +726,33 @@ fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize } } +/// This computes the types of the sub patterns that a constructor should be +/// expanded to. +/// +/// For instance, a tuple pattern (43u32, 'a') has sub pattern types [u32, char]. +fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, + ctor: &Constructor, + ty: Ty<'tcx>) -> Vec> +{ + debug!("constructor_sub_pattern_tys({:?}, {:?})", ctor, ty); + match ty.sty { + ty::TyTuple(ref fs) => fs.into_iter().map(|t| *t).collect(), + ty::TyBox(ty) => vec![ty], + ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor { + Slice(length) => repeat(ty).take(length).collect(), + ConstantValue(_) => vec![], + _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) + }, + ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty], + ty::TyAdt(adt, substs) => { + adt.variants[ctor.variant_index_for_adt(adt)].fields.iter().map(|field| { + field.ty(cx.tcx, substs) + }).collect() + } + _ => vec![], + } +} + fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, ctor: &Constructor, prefix: &[Pattern], @@ -708,19 +804,18 @@ fn range_covered_by_constructor(tcx: TyCtxt, span: Span, Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater) } -fn patterns_for_variant<'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, - subpatterns: &'a [FieldPattern<'tcx>], - arity: usize) - -> Vec<&'a Pattern<'tcx>> +fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>( + subpatterns: &'p [FieldPattern<'tcx>], + wild_patterns: &[&'p Pattern<'tcx>]) + -> Vec<&'p Pattern<'tcx>> { - let mut result = vec![cx.wild_pattern; arity]; + let mut result = wild_patterns.to_owned(); for subpat in subpatterns { result[subpat.field.index()] = &subpat.pattern; } - debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, arity, result); + debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, wild_patterns, result); result } @@ -732,35 +827,41 @@ fn patterns_for_variant<'a, 'tcx>( /// different patterns. /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. -fn specialize<'a, 'tcx>( +fn specialize<'p, 'a: 'p, 'tcx: 'a>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - r: &[&'a Pattern<'tcx>], - constructor: &Constructor, col: usize, arity: usize) - -> Option>> + r: &[&'p Pattern<'tcx>], + constructor: &Constructor, + wild_patterns: &[&'p Pattern<'tcx>]) + -> Option>> { - let pat = &r[col]; + let pat = &r[0]; let head: Option> = match *pat.kind { - PatternKind::Binding { .. } | PatternKind::Wild => - Some(vec![cx.wild_pattern; arity]), + PatternKind::Binding { .. } | PatternKind::Wild => { + Some(wild_patterns.to_owned()) + }, - PatternKind::Variant { adt_def, variant_index, ref subpatterns } => { + PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; if *constructor == Variant(variant.did) { - Some(patterns_for_variant(cx, subpatterns, arity)) + Some(patterns_for_variant(subpatterns, wild_patterns)) } else { None } } - PatternKind::Leaf { ref subpatterns } => Some(patterns_for_variant(cx, subpatterns, arity)), - PatternKind::Deref { ref subpattern } => Some(vec![subpattern]), + PatternKind::Leaf { ref subpatterns } => { + Some(patterns_for_variant(subpatterns, wild_patterns)) + } + PatternKind::Deref { ref subpattern } => { + Some(vec![subpattern]) + } PatternKind::Constant { ref value } => { match *constructor { Slice(..) => match *value { ConstVal::ByteStr(ref data) => { - if arity == data.len() { + if wild_patterns.len() == data.len() { Some(cx.lower_byte_str_pattern(pat)) } else { None @@ -796,11 +897,14 @@ fn specialize<'a, 'tcx>( match *constructor { Slice(..) => { let pat_len = prefix.len() + suffix.len(); - if let Some(slice_count) = arity.checked_sub(pat_len) { + if let Some(slice_count) = wild_patterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { Some( prefix.iter().chain( - repeat(cx.wild_pattern).take(slice_count).chain( + wild_patterns.iter().map(|p| *p) + .skip(prefix.len()) + .take(slice_count) + .chain( suffix.iter() )).collect()) } else { @@ -824,11 +928,10 @@ fn specialize<'a, 'tcx>( } } }; - debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head); + debug!("specialize({:?}, {:?}) = {:?}", r[0], wild_patterns, head); head.map(|mut head| { - head.extend_from_slice(&r[..col]); - head.extend_from_slice(&r[col + 1..]); + head.extend_from_slice(&r[1 ..]); head }) } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 53e83815b465..400af3c70234 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -24,8 +24,9 @@ use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization::{cmt}; use rustc::session::Session; use rustc::traits::Reveal; -use rustc::ty::{self, TyCtxt}; -use rustc_errors::DiagnosticBuilder; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::lint; +use rustc_errors::{Diagnostic, Level, DiagnosticBuilder}; use rustc::hir::def::*; use rustc::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap}; @@ -35,7 +36,7 @@ use rustc_back::slice; use syntax::ast; use syntax::ptr::P; -use syntax_pos::Span; +use syntax_pos::{Span, DUMMY_SP}; struct OuterVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } @@ -50,6 +51,7 @@ impl<'a, 'tcx> Visitor<'tcx> for OuterVisitor<'a, 'tcx> { MatchVisitor { tcx: self.tcx, + tables: self.tcx.body_tables(b), param_env: &ty::ParameterEnvironment::for_item(self.tcx, id) }.visit_body(self.tcx.map.body(b)); } @@ -67,6 +69,7 @@ fn create_e0004<'a>(sess: &'a Session, sp: Span, error_message: String) -> Diagn struct MatchVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, + tables: &'a ty::Tables<'tcx>, param_env: &'a ty::ParameterEnvironment<'tcx> } @@ -80,7 +83,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> { match ex.node { hir::ExprMatch(ref scrut, ref arms, source) => { - self.check_match(scrut, arms, source, ex.span); + self.check_match(scrut, arms, source); } _ => {} } @@ -131,8 +134,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { &self, scrut: &hir::Expr, arms: &[hir::Arm], - source: hir::MatchSource, - span: Span) + source: hir::MatchSource) { for arm in arms { // First, check legality of move bindings. @@ -150,12 +152,13 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { } } - MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| { + let module = self.tcx.map.local_def_id(self.tcx.map.get_module_parent(scrut.id)); + MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| { let mut have_errors = false; let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| ( arm.pats.iter().map(|pat| { - let mut patcx = PatternContext::new(self.tcx); + let mut patcx = PatternContext::new(self.tcx, self.tables); let pattern = expand_pattern(cx, patcx.lower_pattern(&pat)); if !patcx.errors.is_empty() { self.report_inlining_errors(patcx, pat.span); @@ -174,32 +177,14 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { // Fourth, check for unreachable arms. check_arms(cx, &inlined_arms, source); - // Finally, check if the whole match expression is exhaustive. - // Check for empty enum, because is_useful only works on inhabited types. - let pat_ty = self.tcx.tables().node_id_to_type(scrut.id); - if inlined_arms.is_empty() { - if !pat_ty.is_uninhabited(Some(scrut.id), self.tcx) { - // We know the type is inhabited, so this must be wrong - let mut err = create_e0004(self.tcx.sess, span, - format!("non-exhaustive patterns: type {} \ - is non-empty", - pat_ty)); - span_help!(&mut err, span, - "Please ensure that all possible cases are being handled; \ - possibly adding wildcards or more match arms."); - err.emit(); - } - // If the type *is* uninhabited, it's vacuously exhaustive - return; - } - let matrix: Matrix = inlined_arms .iter() .filter(|&&(_, guard)| guard.is_none()) .flat_map(|arm| &arm.0) .map(|pat| vec![pat.0]) .collect(); - check_exhaustive(cx, scrut.span, &matrix, source); + let scrut_ty = self.tables.node_id_to_type(scrut.id); + check_exhaustive(cx, scrut_ty, scrut.span, &matrix, source); }) } @@ -210,13 +195,21 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { "local binding" }; - MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| { - let mut patcx = PatternContext::new(self.tcx); + let module = self.tcx.map.local_def_id(self.tcx.map.get_module_parent(pat.id)); + MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| { + let mut patcx = PatternContext::new(self.tcx, self.tables); + let pattern = patcx.lower_pattern(pat); + let pattern_ty = pattern.ty; let pats : Matrix = vec![vec![ - expand_pattern(cx, patcx.lower_pattern(pat)) + expand_pattern(cx, pattern) ]].into_iter().collect(); - let witness = match is_useful(cx, &pats, &[cx.wild_pattern], ConstructWitness) { + let wild_pattern = Pattern { + ty: pattern_ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + }; + let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) { UsefulWithWitness(witness) => witness, NotUseful => return, Useful => bug!() @@ -237,7 +230,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { fn check_for_bindings_named_the_same_as_variants(cx: &MatchVisitor, pat: &Pat) { pat.walk(|p| { if let PatKind::Binding(hir::BindByValue(hir::MutImmutable), _, name, None) = p.node { - let pat_ty = cx.tcx.tables().pat_ty(p); + let pat_ty = cx.tables.pat_ty(p); if let ty::TyAdt(edef, _) = pat_ty.sty { if edef.is_enum() && edef.variants.iter().any(|variant| { variant.name == name.node && variant.ctor_kind == CtorKind::Const @@ -324,14 +317,16 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, }, hir::MatchSource::Normal => { - let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001, - "unreachable pattern"); - err.span_label(pat.span, &"this is an unreachable pattern"); + let mut diagnostic = Diagnostic::new(Level::Warning, + "unreachable pattern"); + diagnostic.set_span(pat.span); // if we had a catchall pattern, hint at that if let Some(catchall) = catchall { - err.span_note(catchall, "this pattern matches any value"); + diagnostic.span_label(pat.span, &"this is an unreachable pattern"); + diagnostic.span_note(catchall, "this pattern matches any value"); } - err.emit(); + cx.tcx.sess.add_lint_diagnostic(lint::builtin::UNREACHABLE_PATTERNS, + hir_pat.id, diagnostic); }, hir::MatchSource::TryDesugar => { @@ -353,13 +348,19 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, + scrut_ty: Ty<'tcx>, sp: Span, matrix: &Matrix<'a, 'tcx>, source: hir::MatchSource) { - match is_useful(cx, matrix, &[cx.wild_pattern], ConstructWitness) { + let wild_pattern = Pattern { + ty: scrut_ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + }; + match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) { UsefulWithWitness(pats) => { let witnesses = if pats.is_empty() { - vec![cx.wild_pattern] + vec![&wild_pattern] } else { pats.iter().map(|w| w.single_pattern()).collect() }; @@ -456,7 +457,7 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor, for pat in pats { pat.walk(|p| { if let PatKind::Binding(hir::BindByValue(..), _, _, ref sub) = p.node { - let pat_ty = cx.tcx.tables().node_id_to_type(p.id); + let pat_ty = cx.tables.node_id_to_type(p.id); if pat_ty.moves_by_default(cx.tcx, cx.param_env, pat.span) { check_move(p, sub.as_ref().map(|p| &**p)); } @@ -471,13 +472,11 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor, /// /// FIXME: this should be done by borrowck. fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Expr) { - cx.tcx.infer_ctxt(None, Some(cx.param_env.clone()), - Reveal::NotSpecializable).enter(|infcx| { + cx.tcx.infer_ctxt((cx.tables, cx.param_env.clone()), Reveal::NotSpecializable).enter(|infcx| { let mut checker = MutationChecker { cx: cx, }; - let mut visitor = ExprUseVisitor::new(&mut checker, &infcx); - visitor.walk_expr(guard); + ExprUseVisitor::new(&mut checker, &infcx).walk_expr(guard); }); } diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index b24cd261dd58..8c8b2b5da36d 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -16,6 +16,8 @@ register_long_diagnostics! { E0001: r##" +## Note: this error code is no longer emitted by the compiler. + This error suggests that the expression arm corresponding to the noted pattern will never be reached as for all possible values of the expression being matched, one of the preceding patterns will match. @@ -25,10 +27,10 @@ one is too specific or the ordering is incorrect. For example, the following `match` block has too many arms: -```compile_fail,E0001 +``` match Some(0) { Some(bar) => {/* ... */} - None => {/* ... */} + x => {/* ... */} // This handles the `None` case _ => {/* ... */} // All possible cases have already been handled } ``` diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 6b8e0e34c1df..c899d2109f51 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -52,34 +52,24 @@ macro_rules! math { fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, variant_def: DefId) - -> Option<&'tcx Expr> { - let variant_expr = |variants: &'tcx [hir::Variant], id: ast::NodeId | - -> Option<&'tcx Expr> { - for variant in variants { - if variant.node.data.id() == id { - return variant.node.disr_expr.map(|e| { - &tcx.map.body(e).value - }); - } - } - None - }; - + -> Option<(&'tcx Expr, Option<&'a ty::Tables<'tcx>>)> { if let Some(variant_node_id) = tcx.map.as_local_node_id(variant_def) { let enum_node_id = tcx.map.get_parent(variant_node_id); - match tcx.map.find(enum_node_id) { - None => None, - Some(ast_map::NodeItem(it)) => match it.node { - hir::ItemEnum(hir::EnumDef { ref variants }, _) => { - variant_expr(variants, variant_node_id) + if let Some(ast_map::NodeItem(it)) = tcx.map.find(enum_node_id) { + if let hir::ItemEnum(ref edef, _) = it.node { + for variant in &edef.variants { + if variant.node.data.id() == variant_node_id { + return variant.node.disr_expr.map(|e| { + let def_id = tcx.map.body_owner_def_id(e); + (&tcx.map.body(e).value, + tcx.tables.borrow().get(&def_id).cloned()) + }); + } } - _ => None - }, - Some(_) => None + } } - } else { - None } + None } /// * `def_id` is the id of the constant. @@ -90,17 +80,22 @@ fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: Option<&'tcx Substs<'tcx>>) - -> Option<(&'tcx Expr, Option>)> { + -> Option<(&'tcx Expr, + Option<&'a ty::Tables<'tcx>>, + Option>)> { if let Some(node_id) = tcx.map.as_local_node_id(def_id) { match tcx.map.find(node_id) { None => None, - Some(ast_map::NodeItem(it)) => match it.node { - hir::ItemConst(ref ty, body) => { - Some((&tcx.map.body(body).value, - tcx.ast_ty_to_prim_ty(ty))) - } - _ => None - }, + Some(ast_map::NodeItem(&hir::Item { + node: hir::ItemConst(ref ty, body), .. + })) | + Some(ast_map::NodeImplItem(&hir::ImplItem { + node: hir::ImplItemKind::Const(ref ty, body), .. + })) => { + Some((&tcx.map.body(body).value, + tcx.tables.borrow().get(&def_id).cloned(), + tcx.ast_ty_to_prim_ty(ty))) + } Some(ast_map::NodeTraitItem(ti)) => match ti.node { hir::TraitItemKind::Const(ref ty, default) => { if let Some(substs) = substs { @@ -111,6 +106,7 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let trait_id = tcx.map.local_def_id(trait_id); let default_value = default.map(|body| { (&tcx.map.body(body).value, + tcx.tables.borrow().get(&def_id).cloned(), tcx.ast_ty_to_prim_ty(ty)) }); resolve_trait_associated_const(tcx, def_id, default_value, trait_id, substs) @@ -126,18 +122,12 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } _ => None }, - Some(ast_map::NodeImplItem(ii)) => match ii.node { - hir::ImplItemKind::Const(ref ty, body) => { - Some((&tcx.map.body(body).value, - tcx.ast_ty_to_prim_ty(ty))) - } - _ => None - }, Some(_) => None } } else { - let expr_ty = tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| { - (&body.value, Some(tcx.sess.cstore.item_type(tcx, def_id))) + let expr_tables_ty = tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| { + (&body.value, Some(tcx.item_tables(def_id)), + Some(tcx.sess.cstore.item_type(tcx, def_id))) }); match tcx.sess.cstore.describe_def(def_id) { Some(Def::AssociatedConst(_)) => { @@ -148,34 +138,38 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // substitutions for the reference to it. if let Some(trait_id) = trait_id { if let Some(substs) = substs { - resolve_trait_associated_const(tcx, def_id, expr_ty, trait_id, substs) + resolve_trait_associated_const(tcx, def_id, expr_tables_ty, + trait_id, substs) } else { None } } else { - expr_ty + expr_tables_ty } }, - Some(Def::Const(..)) => expr_ty, + Some(Def::Const(..)) => expr_tables_ty, _ => None } } } fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) - -> Option<&'tcx hir::Body> + -> Option<(&'tcx hir::Body, Option<&'a ty::Tables<'tcx>>)> { if let Some(node_id) = tcx.map.as_local_node_id(def_id) { FnLikeNode::from_node(tcx.map.get(node_id)).and_then(|fn_like| { if fn_like.constness() == hir::Constness::Const { - Some(tcx.map.body(fn_like.body())) + Some((tcx.map.body(fn_like.body()), + tcx.tables.borrow().get(&def_id).cloned())) } else { None } }) } else { if tcx.sess.cstore.is_const_fn(def_id) { - tcx.sess.cstore.maybe_get_item_body(tcx, def_id) + tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| { + (body, Some(tcx.item_tables(def_id))) + }) } else { None } @@ -230,31 +224,39 @@ pub fn note_const_eval_err<'a, 'tcx>( } } -pub fn eval_const_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - e: &Expr) -> ConstVal { - match eval_const_expr_checked(tcx, e) { - Ok(r) => r, - // non-const path still needs to be a fatal error, because enums are funky - Err(s) => { - report_const_eval_err(tcx, &s, e.span, "expression").emit(); - match s.kind { - NonConstPath | - UnimplementedConstVal(_) => tcx.sess.abort_if_errors(), - _ => {} - } - Dummy - }, +pub struct ConstContext<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + tables: Option<&'a ty::Tables<'tcx>>, + fn_args: Option> +} + +impl<'a, 'tcx> ConstContext<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, body: hir::BodyId) -> Self { + let def_id = tcx.map.body_owner_def_id(body); + ConstContext { + tcx: tcx, + tables: tcx.tables.borrow().get(&def_id).cloned(), + fn_args: None + } + } + + pub fn with_tables(tcx: TyCtxt<'a, 'tcx, 'tcx>, tables: &'a ty::Tables<'tcx>) -> Self { + ConstContext { + tcx: tcx, + tables: Some(tables), + fn_args: None + } + } + + /// Evaluate a constant expression in a context where the expression isn't + /// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked, + /// but a few places need to evaluate constants during type-checking, like + /// computing the length of an array. (See also the FIXME above EvalHint.) + pub fn eval(&self, e: &Expr, ty_hint: EvalHint<'tcx>) -> EvalResult { + eval_const_expr_partial(self, e, ty_hint) } } -pub fn eval_const_expr_checked<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - e: &Expr) -> EvalResult -{ - eval_const_expr_partial(tcx, e, ExprTypeChecked, None) -} - -pub type FnArgMap<'a> = Option<&'a DefIdMap>; - #[derive(Clone, Debug)] pub struct ConstEvalErr { pub span: Span, @@ -433,20 +435,16 @@ macro_rules! signal { } } -/// Evaluate a constant expression in a context where the expression isn't -/// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked, -/// but a few places need to evaluate constants during type-checking, like -/// computing the length of an array. (See also the FIXME above EvalHint.) -pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - e: &Expr, - ty_hint: EvalHint<'tcx>, - fn_args: FnArgMap) -> EvalResult { +fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, + e: &Expr, + ty_hint: EvalHint<'tcx>) -> EvalResult { + let tcx = cx.tcx; // Try to compute the type of the expression based on the EvalHint. // (See also the definition of EvalHint, and the FIXME above EvalHint.) let ety = match ty_hint { ExprTypeChecked => { // After type-checking, expr_ty is guaranteed to succeed. - Some(tcx.tables().expr_ty(e)) + cx.tables.map(|tables| tables.expr_ty(e)) } UncheckedExprHint(ty) => { // Use the type hint; it's not guaranteed to be right, but it's @@ -457,7 +455,7 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // This expression might not be type-checked, and we have no hint. // Try to query the context for a type anyway; we might get lucky // (for example, if the expression was imported from another crate). - tcx.tables().expr_ty_opt(e) + cx.tables.and_then(|tables| tables.expr_ty_opt(e)) } }; let result = match e.node { @@ -466,33 +464,36 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let hir::ExprLit(ref lit) = inner.node { use syntax::ast::*; use syntax::ast::LitIntType::*; - const I8_OVERFLOW: u128 = i8::max_value() as u128 + 1; - const I16_OVERFLOW: u128 = i16::max_value() as u128 + 1; - const I32_OVERFLOW: u128 = i32::max_value() as u128 + 1; - const I64_OVERFLOW: u128 = i64::max_value() as u128 + 1; - const I128_OVERFLOW: u128 = i128::max_value() as u128 + 1; + const I8_OVERFLOW: u128 = i8::min_value() as u8 as u128; + const I16_OVERFLOW: u128 = i16::min_value() as u16 as u128; + const I32_OVERFLOW: u128 = i32::min_value() as u32 as u128; + const I64_OVERFLOW: u128 = i64::min_value() as u64 as u128; + const I128_OVERFLOW: u128 = i128::min_value() as u128; match (&lit.node, ety.map(|t| &t.sty)) { - (&LitKind::Int(I8_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I8))) | + (&LitKind::Int(I8_OVERFLOW, _), Some(&ty::TyInt(IntTy::I8))) | (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => { return Ok(Integral(I8(i8::min_value()))) }, - (&LitKind::Int(I16_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I16))) | + (&LitKind::Int(I16_OVERFLOW, _), Some(&ty::TyInt(IntTy::I16))) | (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => { return Ok(Integral(I16(i16::min_value()))) }, - (&LitKind::Int(I32_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I32))) | + (&LitKind::Int(I32_OVERFLOW, _), Some(&ty::TyInt(IntTy::I32))) | (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => { return Ok(Integral(I32(i32::min_value()))) }, - (&LitKind::Int(I64_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I64))) | + (&LitKind::Int(I64_OVERFLOW, _), Some(&ty::TyInt(IntTy::I64))) | (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => { return Ok(Integral(I64(i64::min_value()))) }, - (&LitKind::Int(I128_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I128))) | - (&LitKind::Int(I128_OVERFLOW, Signed(IntTy::I128)), _) => { - return Ok(Integral(I128(i128::min_value()))) + (&LitKind::Int(n, _), Some(&ty::TyInt(IntTy::I128))) | + (&LitKind::Int(n, Signed(IntTy::I128)), _) => { + // SNAP: replace n in pattern with I128_OVERFLOW and remove this if. + if n == I128_OVERFLOW { + return Ok(Integral(I128(i128::min_value()))) + } }, - (&LitKind::Int(n, Unsuffixed), Some(&ty::TyInt(IntTy::Is))) | + (&LitKind::Int(n, _), Some(&ty::TyInt(IntTy::Is))) | (&LitKind::Int(n, Signed(IntTy::Is)), _) => { match tcx.sess.target.int_type { IntTy::I16 => if n == I16_OVERFLOW { @@ -510,14 +511,14 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, _ => {}, } } - match eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)? { + match cx.eval(inner, ty_hint)? { Float(f) => Float(-f), Integral(i) => Integral(math!(e, -i)), const_val => signal!(e, NegateOn(const_val)), } } hir::ExprUnary(hir::UnNot, ref inner) => { - match eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)? { + match cx.eval(inner, ty_hint)? { Integral(i) => Integral(math!(e, !i)), Bool(b) => Bool(!b), const_val => signal!(e, NotOn(const_val)), @@ -533,8 +534,8 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // gives us a type through a type-suffix, cast or const def type // we need to re-eval the other value of the BinOp if it was // not inferred - match (eval_const_expr_partial(tcx, &a, ty_hint, fn_args)?, - eval_const_expr_partial(tcx, &b, b_ty, fn_args)?) { + match (cx.eval(a, ty_hint)?, + cx.eval(b, b_ty)?) { (Float(a), Float(b)) => { use std::cmp::Ordering::*; match op.node { @@ -604,13 +605,13 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let base_hint = if let ExprTypeChecked = ty_hint { ExprTypeChecked } else { - match tcx.tables().expr_ty_opt(&base) { + match cx.tables.and_then(|tables| tables.expr_ty_opt(&base)) { Some(t) => UncheckedExprHint(t), None => ty_hint } }; - let val = match eval_const_expr_partial(tcx, &base, base_hint, fn_args) { + let val = match cx.eval(base, base_hint) { Ok(val) => val, Err(ConstEvalErr { kind: ErroneousReferencedConstant( box ConstEvalErr { kind: TypeMismatch(_, val), .. }), .. }) | @@ -623,7 +624,7 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // we had a type hint, so we can't have an unknown type None => bug!(), }; - eval_const_expr_partial(tcx, &base, hint, fn_args)? + cx.eval(base, hint)? }, Err(e) => return Err(e), }; @@ -633,22 +634,29 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } hir::ExprPath(ref qpath) => { - let def = tcx.tables().qpath_def(qpath, e.id); + let def = cx.tables.map(|tables| tables.qpath_def(qpath, e.id)).unwrap_or_else(|| { + // There are no tables so we can only handle already-resolved HIR. + match *qpath { + hir::QPath::Resolved(_, ref path) => path.def, + hir::QPath::TypeRelative(..) => Def::Err + } + }); match def { Def::Const(def_id) | Def::AssociatedConst(def_id) => { let substs = if let ExprTypeChecked = ty_hint { - Some(tcx.tables().node_id_item_substs(e.id) + Some(cx.tables.and_then(|tables| tables.node_id_item_substs(e.id)) .unwrap_or_else(|| tcx.intern_substs(&[]))) } else { None }; - if let Some((expr, ty)) = lookup_const_by_id(tcx, def_id, substs) { + if let Some((expr, tables, ty)) = lookup_const_by_id(tcx, def_id, substs) { let item_hint = match ty { Some(ty) => ty_hint.checked_or(ty), None => ty_hint, }; - match eval_const_expr_partial(tcx, expr, item_hint, None) { + let cx = ConstContext { tcx: tcx, tables: tables, fn_args: None }; + match cx.eval(expr, item_hint) { Ok(val) => val, Err(err) => { debug!("bad reference: {:?}, {:?}", err.description(), err.span); @@ -660,8 +668,9 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } }, Def::VariantCtor(variant_def, ..) => { - if let Some(const_expr) = lookup_variant_by_id(tcx, variant_def) { - match eval_const_expr_partial(tcx, const_expr, ty_hint, None) { + if let Some((expr, tables)) = lookup_variant_by_id(tcx, variant_def) { + let cx = ConstContext { tcx: tcx, tables: tables, fn_args: None }; + match cx.eval(expr, ty_hint) { Ok(val) => val, Err(err) => { debug!("bad reference: {:?}, {:?}", err.description(), err.span); @@ -673,11 +682,11 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } Def::StructCtor(..) => { - ConstVal::Struct(e.id) + ConstVal::Struct(Default::default()) } Def::Local(def_id) => { - debug!("Def::Local({:?}): {:?}", def_id, fn_args); - if let Some(val) = fn_args.and_then(|args| args.get(&def_id)) { + debug!("Def::Local({:?}): {:?}", def_id, cx.fn_args); + if let Some(val) = cx.fn_args.as_ref().and_then(|args| args.get(&def_id)) { val.clone() } else { signal!(e, NonConstPath); @@ -690,14 +699,14 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } hir::ExprCall(ref callee, ref args) => { let sub_ty_hint = ty_hint.erase_hint(); - let callee_val = eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)?; + let callee_val = cx.eval(callee, sub_ty_hint)?; let did = match callee_val { Function(did) => did, Struct(_) => signal!(e, UnimplementedConstVal("tuple struct constructors")), callee => signal!(e, CallOn(callee)), }; - let body = match lookup_const_fn_by_id(tcx, did) { - Some(body) => body, + let (body, tables) = match lookup_const_fn_by_id(tcx, did) { + Some(x) => x, None => signal!(e, NonConstPath), }; @@ -710,19 +719,19 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut call_args = DefIdMap(); for (arg, arg_expr) in arg_defs.into_iter().zip(args.iter()) { let arg_hint = ty_hint.erase_hint(); - let arg_val = eval_const_expr_partial( - tcx, - arg_expr, - arg_hint, - fn_args - )?; + let arg_val = cx.eval(arg_expr, arg_hint)?; debug!("const call arg: {:?}", arg); if let Some(def_id) = arg { assert!(call_args.insert(def_id, arg_val).is_none()); } } debug!("const call({:?})", call_args); - eval_const_expr_partial(tcx, &body.value, ty_hint, Some(&call_args))? + let callee_cx = ConstContext { + tcx: tcx, + tables: tables, + fn_args: Some(call_args) + }; + callee_cx.eval(&body.value, ty_hint)? }, hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ety) { Ok(val) => val, @@ -730,46 +739,49 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }, hir::ExprBlock(ref block) => { match block.expr { - Some(ref expr) => eval_const_expr_partial(tcx, &expr, ty_hint, fn_args)?, + Some(ref expr) => cx.eval(expr, ty_hint)?, None => signal!(e, UnimplementedConstVal("empty block")), } } - hir::ExprType(ref e, _) => eval_const_expr_partial(tcx, &e, ty_hint, fn_args)?, - hir::ExprTup(_) => Tuple(e.id), - hir::ExprStruct(..) => Struct(e.id), + hir::ExprType(ref e, _) => cx.eval(e, ty_hint)?, + hir::ExprTup(ref fields) => { + let field_hint = ty_hint.erase_hint(); + Tuple(fields.iter().map(|e| cx.eval(e, field_hint)).collect::>()?) + } + hir::ExprStruct(_, ref fields, _) => { + let field_hint = ty_hint.erase_hint(); + Struct(fields.iter().map(|f| { + cx.eval(&f.expr, field_hint).map(|v| (f.name.node, v)) + }).collect::>()?) + } hir::ExprIndex(ref arr, ref idx) => { if !tcx.sess.features.borrow().const_indexing { signal!(e, IndexOpFeatureGated); } let arr_hint = ty_hint.erase_hint(); - let arr = eval_const_expr_partial(tcx, arr, arr_hint, fn_args)?; + let arr = cx.eval(arr, arr_hint)?; let idx_hint = ty_hint.checked_or(tcx.types.usize); - let idx = match eval_const_expr_partial(tcx, idx, idx_hint, fn_args)? { + let idx = match cx.eval(idx, idx_hint)? { Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type), Integral(_) => bug!(), _ => signal!(idx, IndexNotInt), }; assert_eq!(idx as usize as u64, idx); match arr { - Array(_, n) if idx >= n => { - signal!(e, IndexOutOfBounds { len: n, index: idx }) + Array(ref v) => { + if let Some(elem) = v.get(idx as usize) { + elem.clone() + } else { + let n = v.len() as u64; + assert_eq!(n as usize as u64, n); + signal!(e, IndexOutOfBounds { len: n, index: idx }) + } } - Array(v, n) => if let hir::ExprArray(ref v) = tcx.map.expect_expr(v).node { - assert_eq!(n as usize as u64, n); - eval_const_expr_partial(tcx, &v[idx as usize], ty_hint, fn_args)? - } else { - bug!() - }, - Repeat(_, n) if idx >= n => { + Repeat(.., n) if idx >= n => { signal!(e, IndexOutOfBounds { len: n, index: idx }) } - Repeat(elem, _) => eval_const_expr_partial( - tcx, - &tcx.map.expect_expr(elem), - ty_hint, - fn_args, - )?, + Repeat(ref elem, _) => (**elem).clone(), ByteStr(ref data) if idx >= data.len() as u64 => { signal!(e, IndexOutOfBounds { len: data.len() as u64, index: idx }) @@ -781,31 +793,38 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, _ => signal!(e, IndexedNonVec), } } - hir::ExprArray(ref v) => Array(e.id, v.len() as u64), - hir::ExprRepeat(_, n) => { + hir::ExprArray(ref v) => { + let elem_hint = ty_hint.erase_hint(); + Array(v.iter().map(|e| cx.eval(e, elem_hint)).collect::>()?) + } + hir::ExprRepeat(ref elem, count) => { + let elem_hint = ty_hint.erase_hint(); let len_hint = ty_hint.checked_or(tcx.types.usize); - let n = &tcx.map.body(n).value; - Repeat( - e.id, - match eval_const_expr_partial(tcx, n, len_hint, fn_args)? { - Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type), - Integral(_) => signal!(e, RepeatCountNotNatural), - _ => signal!(e, RepeatCountNotInt), - }, - ) + let n = if let Some(ty) = ety { + // For cross-crate constants, we have the type already, + // but not the body for `count`, so use the type. + match ty.sty { + ty::TyArray(_, n) => n as u64, + _ => bug!() + } + } else { + let n = &tcx.map.body(count).value; + match ConstContext::new(tcx, count).eval(n, len_hint)? { + Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type), + Integral(_) => signal!(e, RepeatCountNotNatural), + _ => signal!(e, RepeatCountNotInt), + } + }; + Repeat(Box::new(cx.eval(elem, elem_hint)?), n) }, hir::ExprTupField(ref base, index) => { let base_hint = ty_hint.erase_hint(); - let c = eval_const_expr_partial(tcx, base, base_hint, fn_args)?; - if let Tuple(tup_id) = c { - if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node { - if index.node < fields.len() { - eval_const_expr_partial(tcx, &fields[index.node], ty_hint, fn_args)? - } else { - signal!(e, TupleIndexOutOfBounds); - } + let c = cx.eval(base, base_hint)?; + if let Tuple(ref fields) = c { + if let Some(elem) = fields.get(index.node) { + elem.clone() } else { - bug!() + signal!(e, TupleIndexOutOfBounds); } } else { signal!(base, ExpectedConstTuple); @@ -813,20 +832,12 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } hir::ExprField(ref base, field_name) => { let base_hint = ty_hint.erase_hint(); - // Get the base expression if it is a struct and it is constant - let c = eval_const_expr_partial(tcx, base, base_hint, fn_args)?; - if let Struct(struct_id) = c { - if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node { - // Check that the given field exists and evaluate it - // if the idents are compared run-pass/issue-19244 fails - if let Some(f) = fields.iter().find(|f| f.name.node - == field_name.node) { - eval_const_expr_partial(tcx, &f.expr, ty_hint, fn_args)? - } else { - signal!(e, MissingStructField); - } + let c = cx.eval(base, base_hint)?; + if let Struct(ref fields) = c { + if let Some(f) = fields.get(&field_name.node) { + f.clone() } else { - bug!() + signal!(e, MissingStructField); } } else { signal!(base, ExpectedConstStruct); @@ -909,17 +920,17 @@ fn infer<'a, 'tcx>(i: ConstInt, fn resolve_trait_associated_const<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_item_id: DefId, - default_value: Option<(&'tcx Expr, Option>)>, + default_value: Option<(&'tcx Expr, Option<&'a ty::Tables<'tcx>>, Option>)>, trait_id: DefId, rcvr_substs: &'tcx Substs<'tcx> -) -> Option<(&'tcx Expr, Option>)> +) -> Option<(&'tcx Expr, Option<&'a ty::Tables<'tcx>>, Option>)> { let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, rcvr_substs)); debug!("resolve_trait_associated_const: trait_ref={:?}", trait_ref); tcx.populate_implementations_for_trait_if_necessary(trait_id); - tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| { + tcx.infer_ctxt((), Reveal::NotSpecializable).enter(|infcx| { let mut selcx = traits::SelectionContext::new(&infcx); let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), trait_ref.to_poly_trait_predicate()); @@ -1160,36 +1171,40 @@ pub fn compare_const_vals(tcx: TyCtxt, span: Span, a: &ConstVal, b: &ConstVal) } } -pub fn compare_lit_exprs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - span: Span, - a: &Expr, - b: &Expr) -> Result { - let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked, None) { - Ok(a) => a, - Err(e) => { - report_const_eval_err(tcx, &e, a.span, "expression").emit(); - return Err(ErrorReported); - } - }; - let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked, None) { - Ok(b) => b, - Err(e) => { - report_const_eval_err(tcx, &e, b.span, "expression").emit(); - return Err(ErrorReported); - } - }; - compare_const_vals(tcx, span, &a, &b) +impl<'a, 'tcx> ConstContext<'a, 'tcx> { + pub fn compare_lit_exprs(&self, + span: Span, + a: &Expr, + b: &Expr) -> Result { + let tcx = self.tcx; + let a = match self.eval(a, ExprTypeChecked) { + Ok(a) => a, + Err(e) => { + report_const_eval_err(tcx, &e, a.span, "expression").emit(); + return Err(ErrorReported); + } + }; + let b = match self.eval(b, ExprTypeChecked) { + Ok(b) => b, + Err(e) => { + report_const_eval_err(tcx, &e, b.span, "expression").emit(); + return Err(ErrorReported); + } + }; + compare_const_vals(tcx, span, &a, &b) + } } /// Returns the value of the length-valued expression pub fn eval_length<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - count_expr: &hir::Expr, + count: hir::BodyId, reason: &str) -> Result { let hint = UncheckedExprHint(tcx.types.usize); - match eval_const_expr_partial(tcx, count_expr, hint, None) { + let count_expr = &tcx.map.body(count).value; + match ConstContext::new(tcx, count).eval(count_expr, hint) { Ok(Integral(Usize(count))) => { let val = count.as_u64(tcx.sess.target.uint_type); assert_eq!(val as usize as u64, val); diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index b122d97a702f..fbd15b6eb103 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -13,7 +13,8 @@ use eval; use rustc::lint; use rustc::middle::const_val::ConstVal; use rustc::mir::{Field, BorrowKind, Mutability}; -use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; +use rustc::ty::{self, TyCtxt, AdtDef, Ty, TypeVariants, Region}; +use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; @@ -67,6 +68,7 @@ pub enum PatternKind<'tcx> { /// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants Variant { adt_def: &'tcx AdtDef, + substs: &'tcx Substs<'tcx>, variant_index: usize, subpatterns: Vec>, }, @@ -117,8 +119,7 @@ fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result { ConstVal::Tuple(_) | ConstVal::Function(_) | ConstVal::Array(..) | - ConstVal::Repeat(..) | - ConstVal::Dummy => bug!("{:?} not printable in a pattern", value) + ConstVal::Repeat(..) => bug!("{:?} not printable in a pattern", value) } } @@ -259,12 +260,15 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { pub struct PatternContext<'a, 'gcx: 'tcx, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'gcx, 'tcx>, + pub tables: &'a ty::Tables<'gcx>, pub errors: Vec, } impl<'a, 'gcx, 'tcx> Pattern<'tcx> { - pub fn from_hir(tcx: TyCtxt<'a, 'gcx, 'tcx>, pat: &hir::Pat) -> Self { - let mut pcx = PatternContext::new(tcx); + pub fn from_hir(tcx: TyCtxt<'a, 'gcx, 'tcx>, + tables: &'a ty::Tables<'gcx>, + pat: &hir::Pat) -> Self { + let mut pcx = PatternContext::new(tcx, tables); let result = pcx.lower_pattern(pat); if !pcx.errors.is_empty() { span_bug!(pat.span, "encountered errors lowering pattern: {:?}", pcx.errors) @@ -275,12 +279,12 @@ impl<'a, 'gcx, 'tcx> Pattern<'tcx> { } impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Self { - PatternContext { tcx: tcx, errors: vec![] } + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, tables: &'a ty::Tables<'gcx>) -> Self { + PatternContext { tcx: tcx, tables: tables, errors: vec![] } } pub fn lower_pattern(&mut self, pat: &hir::Pat) -> Pattern<'tcx> { - let mut ty = self.tcx.tables().node_id_to_type(pat.id); + let mut ty = self.tables.node_id_to_type(pat.id); let kind = match pat.node { PatKind::Wild => PatternKind::Wild, @@ -307,7 +311,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { } PatKind::Slice(ref prefix, ref slice, ref suffix) => { - let ty = self.tcx.tables().node_id_to_type(pat.id); + let ty = self.tables.node_id_to_type(pat.id); match ty.sty { ty::TyRef(_, mt) => PatternKind::Deref { @@ -332,7 +336,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { } PatKind::Tuple(ref subpatterns, ddpos) => { - let ty = self.tcx.tables().node_id_to_type(pat.id); + let ty = self.tables.node_id_to_type(pat.id); match ty.sty { ty::TyTuple(ref tys) => { let subpatterns = @@ -353,7 +357,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { PatKind::Binding(bm, def_id, ref ident, ref sub) => { let id = self.tcx.map.as_local_node_id(def_id).unwrap(); - let var_ty = self.tcx.tables().node_id_to_type(pat.id); + let var_ty = self.tables.node_id_to_type(pat.id); let region = match var_ty.sty { ty::TyRef(r, _) => Some(r), _ => None, @@ -390,9 +394,8 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { } PatKind::TupleStruct(ref qpath, ref subpatterns, ddpos) => { - let def = self.tcx.tables().qpath_def(qpath, pat.id); - let pat_ty = self.tcx.tables().node_id_to_type(pat.id); - let adt_def = match pat_ty.sty { + let def = self.tables.qpath_def(qpath, pat.id); + let adt_def = match ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT"), }; @@ -406,13 +409,12 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { pattern: self.lower_pattern(field), }) .collect(); - self.lower_variant_or_leaf(def, subpatterns) + self.lower_variant_or_leaf(def, ty, subpatterns) } PatKind::Struct(ref qpath, ref fields, _) => { - let def = self.tcx.tables().qpath_def(qpath, pat.id); - let pat_ty = self.tcx.tables().node_id_to_type(pat.id); - let adt_def = match pat_ty.sty { + let def = self.tables.qpath_def(qpath, pat.id); + let adt_def = match ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => { span_bug!( @@ -439,7 +441,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { }) .collect(); - self.lower_variant_or_leaf(def, subpatterns) + self.lower_variant_or_leaf(def, ty, subpatterns) } }; @@ -529,6 +531,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { fn lower_variant_or_leaf( &mut self, def: Def, + ty: Ty<'tcx>, subpatterns: Vec>) -> PatternKind<'tcx> { @@ -537,8 +540,14 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { let enum_id = self.tcx.parent_def_id(variant_id).unwrap(); let adt_def = self.tcx.lookup_adt_def(enum_id); if adt_def.variants.len() > 1 { + let substs = match ty.sty { + TypeVariants::TyAdt(_, substs) => substs, + TypeVariants::TyFnDef(_, substs, _) => substs, + _ => bug!("inappropriate type for def: {:?}", ty.sty), + }; PatternKind::Variant { adt_def: adt_def, + substs: substs, variant_index: adt_def.variant_index_with_id(variant_id), subpatterns: subpatterns, } @@ -562,15 +571,21 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { pat_id: ast::NodeId, span: Span) -> Pattern<'tcx> { - let def = self.tcx.tables().qpath_def(qpath, id); + let ty = self.tables.node_id_to_type(id); + let def = self.tables.qpath_def(qpath, id); let kind = match def { Def::Const(def_id) | Def::AssociatedConst(def_id) => { let tcx = self.tcx.global_tcx(); - let substs = tcx.tables().node_id_item_substs(id) + let substs = self.tables.node_id_item_substs(id) .unwrap_or_else(|| tcx.intern_substs(&[])); match eval::lookup_const_by_id(tcx, def_id, Some(substs)) { - Some((const_expr, _const_ty)) => { - return self.lower_const_expr(const_expr, pat_id, span); + Some((const_expr, const_tables, _const_ty)) => { + // Enter the inlined constant's tables temporarily. + let old_tables = self.tables; + self.tables = const_tables.expect("missing tables after typeck"); + let pat = self.lower_const_expr(const_expr, pat_id, span); + self.tables = old_tables; + return pat; } None => { self.errors.push(PatternError::StaticInPattern(span)); @@ -578,18 +593,19 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { } } } - _ => self.lower_variant_or_leaf(def, vec![]) + _ => self.lower_variant_or_leaf(def, ty, vec![]), }; Pattern { span: span, - ty: self.tcx.tables().node_id_to_type(id), + ty: ty, kind: Box::new(kind), } } fn lower_lit(&mut self, expr: &hir::Expr) -> PatternKind<'tcx> { - match eval::eval_const_expr_checked(self.tcx.global_tcx(), expr) { + let const_cx = eval::ConstContext::with_tables(self.tcx.global_tcx(), self.tables); + match const_cx.eval(expr, eval::EvalHint::ExprTypeChecked) { Ok(value) => { PatternKind::Constant { value: value } } @@ -605,7 +621,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { pat_id: ast::NodeId, span: Span) -> Pattern<'tcx> { - let pat_ty = self.tcx.tables().expr_ty(expr); + let pat_ty = self.tables.expr_ty(expr); debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id); match pat_ty.sty { ty::TyFloat(_) => { @@ -651,7 +667,8 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { hir::ExprPath(ref qpath) => qpath, _ => bug!() }; - let def = self.tcx.tables().qpath_def(qpath, callee.id); + let ty = self.tables.node_id_to_type(callee.id); + let def = self.tables.qpath_def(qpath, callee.id); match def { Def::Fn(..) | Def::Method(..) => self.lower_lit(expr), _ => { @@ -661,14 +678,13 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { pattern: self.lower_const_expr(expr, pat_id, span) } }).collect(); - self.lower_variant_or_leaf(def, subpatterns) + self.lower_variant_or_leaf(def, ty, subpatterns) } } } hir::ExprStruct(ref qpath, ref fields, None) => { - let def = self.tcx.tables().qpath_def(qpath, expr.id); - let pat_ty = self.tcx.tables().node_id_to_type(expr.id); + let def = self.tables.qpath_def(qpath, expr.id); let adt_def = match pat_ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => { @@ -696,7 +712,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { }) .collect(); - self.lower_variant_or_leaf(def, subpatterns) + self.lower_variant_or_leaf(def, pat_ty, subpatterns) } hir::ExprArray(ref exprs) => { @@ -776,8 +792,9 @@ macro_rules! CloneImpls { } CloneImpls!{ <'tcx> - Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal, - Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef + Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal, Region, + Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef, + &'tcx Substs<'tcx>, &'tcx Kind<'tcx> } impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> { @@ -828,10 +845,12 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { }, PatternKind::Variant { adt_def, + substs, variant_index, ref subpatterns, } => PatternKind::Variant { adt_def: adt_def.fold_with(folder), + substs: substs.fold_with(folder), variant_index: variant_index.fold_with(folder), subpatterns: subpatterns.fold_with(folder) }, diff --git a/src/librustc_const_math/int.rs b/src/librustc_const_math/int.rs index 59eb4b70aa8f..53f185b661b4 100644 --- a/src/librustc_const_math/int.rs +++ b/src/librustc_const_math/int.rs @@ -661,7 +661,7 @@ impl ::std::ops::Neg for ConstInt { a@U8(0) | a@U16(0) | a@U32(0) | a@U64(0) | a@U128(0) | a@Usize(Us16(0)) | a@Usize(Us32(0)) | a@Usize(Us64(0)) => Ok(a), U8(_) | U16(_) | U32(_) | U64(_) | U128(_) | Usize(_) => Err(UnsignedNegation), - Infer(a @ 0...ubounds::I64MAX) => Ok(InferSigned(-(a as i128))), + Infer(a @ 0...ubounds::I128MAX) => Ok(InferSigned(-(a as i128))), Infer(_) => Err(Overflow(Op::Neg)), InferSigned(a) => Ok(InferSigned(overflowing!(a.overflowing_neg(), Op::Neg))), } diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index 597344a2c82d..cd933c005994 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -26,6 +26,7 @@ #![feature(rustc_private)] #![feature(staged_api)] #![feature(const_fn)] +#![cfg_attr(not(stage0), feature(i128))] #[macro_use] extern crate log; #[macro_use] extern crate syntax; diff --git a/src/librustc_data_structures/accumulate_vec.rs b/src/librustc_data_structures/accumulate_vec.rs index 937cb3f60074..78af655852d1 100644 --- a/src/librustc_data_structures/accumulate_vec.rs +++ b/src/librustc_data_structures/accumulate_vec.rs @@ -19,6 +19,7 @@ use std::ops::{Deref, DerefMut}; use std::iter::{self, IntoIterator, FromIterator}; use std::slice; use std::vec; +use std::collections::range::RangeArgument; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; @@ -71,6 +72,19 @@ impl AccumulateVec { AccumulateVec::Heap(ref mut vec) => vec.pop(), } } + + pub fn drain(&mut self, range: R) -> Drain + where R: RangeArgument + { + match *self { + AccumulateVec::Array(ref mut v) => { + Drain::Array(v.drain(range)) + }, + AccumulateVec::Heap(ref mut v) => { + Drain::Heap(v.drain(range)) + }, + } + } } impl Deref for AccumulateVec { @@ -132,6 +146,31 @@ impl Iterator for IntoIter { } } +pub enum Drain<'a, A: Array> + where A::Element: 'a +{ + Array(array_vec::Drain<'a, A>), + Heap(vec::Drain<'a, A::Element>), +} + +impl<'a, A: Array> Iterator for Drain<'a, A> { + type Item = A::Element; + + fn next(&mut self) -> Option { + match *self { + Drain::Array(ref mut drain) => drain.next(), + Drain::Heap(ref mut drain) => drain.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match *self { + Drain::Array(ref drain) => drain.size_hint(), + Drain::Heap(ref drain) => drain.size_hint(), + } + } +} + impl IntoIterator for AccumulateVec { type Item = A::Element; type IntoIter = IntoIter; diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index 631cf2cfcf6d..844e9041d202 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -12,12 +12,13 @@ use std::marker::Unsize; use std::iter::Extend; -use std::ptr::{self, drop_in_place}; +use std::ptr::{self, drop_in_place, Shared}; use std::ops::{Deref, DerefMut, Range}; use std::hash::{Hash, Hasher}; use std::slice; use std::fmt; use std::mem; +use std::collections::range::RangeArgument; pub unsafe trait Array { type Element; @@ -103,6 +104,44 @@ impl ArrayVec { None } } + + pub fn drain(&mut self, range: R) -> Drain + where R: RangeArgument + { + // Memory safety + // + // When the Drain is first created, it shortens the length of + // the source vector to make sure no uninitalized or moved-from elements + // are accessible at all if the Drain's destructor never gets to run. + // + // Drain will ptr::read out the values to remove. + // When finished, remaining tail of the vec is copied back to cover + // the hole, and the vector length is restored to the new length. + // + let len = self.len(); + let start = *range.start().unwrap_or(&0); + let end = *range.end().unwrap_or(&len); + assert!(start <= end); + assert!(end <= len); + + unsafe { + // set self.vec length's to start, to be safe in case Drain is leaked + self.set_len(start); + // Use the borrow in the IterMut to indicate borrowing behavior of the + // whole Drain iterator (like &mut T). + let range_slice = { + let arr = &mut self.values as &mut [ManuallyDrop<_>]; + slice::from_raw_parts_mut(arr.as_mut_ptr().offset(start as isize), + end - start) + }; + Drain { + tail_start: end, + tail_len: len - end, + iter: range_slice.iter(), + array_vec: Shared::new(self as *mut _), + } + } + } } impl Default for ArrayVec @@ -179,6 +218,51 @@ impl Iterator for Iter { } } +pub struct Drain<'a, A: Array> + where A::Element: 'a +{ + tail_start: usize, + tail_len: usize, + iter: slice::Iter<'a, ManuallyDrop>, + array_vec: Shared>, +} + +impl<'a, A: Array> Iterator for Drain<'a, A> { + type Item = A::Element; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(|elt| unsafe { ptr::read(elt as *const ManuallyDrop<_>).value }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a, A: Array> Drop for Drain<'a, A> { + fn drop(&mut self) { + // exhaust self first + while let Some(_) = self.next() {} + + if self.tail_len > 0 { + unsafe { + let source_array_vec = &mut **self.array_vec; + // memmove back untouched tail, update to new length + let start = source_array_vec.len(); + let tail = self.tail_start; + { + let mut arr = &mut source_array_vec.values as &mut [ManuallyDrop<_>]; + let src = arr.as_ptr().offset(tail as isize); + let dst = arr.as_mut_ptr().offset(start as isize); + ptr::copy(src, dst, self.tail_len); + }; + source_array_vec.set_len(start + self.tail_len); + } + } + } +} + impl IntoIterator for ArrayVec { type Item = A::Element; type IntoIter = Iter; diff --git a/src/librustc_data_structures/blake2b.rs b/src/librustc_data_structures/blake2b.rs index 8c82c135dc42..31492e262194 100644 --- a/src/librustc_data_structures/blake2b.rs +++ b/src/librustc_data_structures/blake2b.rs @@ -113,17 +113,20 @@ fn blake2b_compress(ctx: &mut Blake2bCtx, last: bool) { } { - // Re-interpret the input buffer in the state as u64s + // Re-interpret the input buffer in the state as an array + // of little-endian u64s, converting them to machine + // endianness. It's OK to modify the buffer in place + // since this is the last time this data will be accessed + // before it's overwritten. + let m: &mut [u64; 16] = unsafe { let b: &mut [u8; 128] = &mut ctx.b; ::std::mem::transmute(b) }; - // It's OK to modify the buffer in place since this is the last time - // this data will be accessed before it's overwritten if cfg!(target_endian = "big") { for word in &mut m[..] { - *word = word.to_be(); + *word = u64::from_le(*word); } } @@ -209,9 +212,10 @@ fn blake2b_final(ctx: &mut Blake2bCtx) blake2b_compress(ctx, true); + // Modify our buffer to little-endian format as it will be read + // as a byte array. It's OK to modify the buffer in place since + // this is the last time this data will be accessed. if cfg!(target_endian = "big") { - // Make sure that the data is in memory in little endian format, as is - // demanded by BLAKE2 for word in &mut ctx.h { *word = word.to_le(); } diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index d3ec674daed4..4623e52ffc2f 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -23,8 +23,10 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] +#![feature(shared)] +#![feature(collections_range)] #![feature(nonzero)] #![feature(rustc_private)] #![feature(staged_api)] diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index e9c14b4f99c2..442c139f14c0 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -716,8 +716,7 @@ pub fn phase_2_configure_and_expand(sess: &Session, is_proc_macro_crate, is_test_crate, num_crate_types, - sess.diagnostic(), - &sess.features.borrow()) + sess.diagnostic()) }); } @@ -1335,9 +1334,10 @@ pub fn build_output_filenames(input: &Input, .values() .filter(|a| a.is_none()) .count(); - let ofile = if unnamed_output_types > 1 { - sess.warn("ignoring specified output filename because multiple outputs were \ - requested"); + let ofile = if unnamed_output_types > 1 && + sess.opts.output_types.contains_key(&OutputType::Exe) { + sess.warn("ignoring specified output filename for 'link' output because multiple \ + outputs were requested"); None } else { Some(out_file.clone()) diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index ca31022b04c6..25049b3d2bad 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -21,7 +21,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(box_syntax)] #![feature(libc)] diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index cc288619cdee..afacfb6e3f9f 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -39,6 +39,7 @@ use syntax_pos; use graphviz as dot; +use std::cell::Cell; use std::fs::File; use std::io::{self, Write}; use std::iter; @@ -236,7 +237,11 @@ impl PpSourceMode { arenas, id, |tcx, _, _, _| { - let annotation = TypedAnnotation { tcx: tcx }; + let empty_tables = ty::Tables::empty(); + let annotation = TypedAnnotation { + tcx: tcx, + tables: Cell::new(&empty_tables) + }; let _ignore = tcx.dep_graph.in_ignore(); f(&annotation, payload, ast_map.forest.krate()) }), @@ -488,6 +493,7 @@ impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> { struct TypedAnnotation<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, + tables: Cell<&'a ty::Tables<'tcx>>, } impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> { @@ -511,7 +517,13 @@ impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> { impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> { fn nested(&self, state: &mut pprust_hir::State, nested: pprust_hir::Nested) -> io::Result<()> { - pprust_hir::PpAnn::nested(&self.tcx.map, state, nested) + let old_tables = self.tables.get(); + if let pprust_hir::Nested::Body(id) = nested { + self.tables.set(self.tcx.body_tables(id)); + } + pprust_hir::PpAnn::nested(&self.tcx.map, state, nested)?; + self.tables.set(old_tables); + Ok(()) } fn pre(&self, s: &mut pprust_hir::State, node: pprust_hir::AnnNode) -> io::Result<()> { match node { @@ -525,7 +537,7 @@ impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> { pp::space(&mut s.s)?; pp::word(&mut s.s, "as")?; pp::space(&mut s.s)?; - pp::word(&mut s.s, &self.tcx.tables().expr_ty(expr).to_string())?; + pp::word(&mut s.s, &self.tables.get().expr_ty(expr).to_string())?; s.pclose() } _ => Ok(()), @@ -751,7 +763,7 @@ fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec, } blocks::Code::FnLike(fn_like) => { let (bccx, analysis_data) = - borrowck::build_borrowck_dataflow_data_for_fn(tcx, fn_like.to_fn_parts(), &cfg); + borrowck::build_borrowck_dataflow_data_for_fn(tcx, fn_like.body(), &cfg); let lcfg = borrowck_dot::DataflowLabeller { inner: lcfg, diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 887e586a3112..ede35d052ad5 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -149,7 +149,7 @@ fn test_env(source_string: &str, index, "test_crate", |tcx| { - tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| { + tcx.infer_ctxt((), Reveal::NotSpecializable).enter(|infcx| { body(Env { infcx: &infcx }); let free_regions = FreeRegionMap::new(); diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 808fe504b95c..77c6c3683641 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -703,6 +703,40 @@ impl EmitterWriter { } } + /// Add a left margin to every line but the first, given a padding length and the label being + /// displayed. + fn msg_with_padding(&self, msg: &str, padding: usize, label: &str) -> String { + // The extra 5 ` ` is padding that's always needed to align to the `note: `: + // + // error: message + // --> file.rs:13:20 + // | + // 13 | + // | ^^^^ + // | + // = note: multiline + // message + // ++^^^----xx + // | | | | + // | | | magic `2` + // | | length of label + // | magic `3` + // `max_line_num_len` + let padding = (0..padding + label.len() + 5) + .map(|_| " ") + .collect::(); + + msg.split('\n').enumerate().fold("".to_owned(), |mut acc, x| { + if x.0 != 0 { + acc.push_str("\n"); + // Align every line with first one. + acc.push_str(&padding); + } + acc.push_str(&x.1); + acc + }) + } + fn emit_message_default(&mut self, msp: &MultiSpan, msg: &str, @@ -721,7 +755,9 @@ impl EmitterWriter { draw_note_separator(&mut buffer, 0, max_line_num_len + 1); buffer.append(0, &level.to_string(), Style::HeaderMsg); buffer.append(0, ": ", Style::NoStyle); - buffer.append(0, msg, Style::NoStyle); + + let message = self.msg_with_padding(msg, max_line_num_len, "note"); + buffer.append(0, &message, Style::NoStyle); } else { buffer.append(0, &level.to_string(), Style::Level(level.clone())); match code { @@ -854,7 +890,9 @@ impl EmitterWriter { buffer.append(0, &level.to_string(), Style::Level(level.clone())); buffer.append(0, ": ", Style::HeaderMsg); - buffer.append(0, msg, Style::HeaderMsg); + + let message = self.msg_with_padding(msg, max_line_num_len, "suggestion"); + buffer.append(0, &message, Style::HeaderMsg); let lines = cm.span_to_lines(primary_span).unwrap(); @@ -930,7 +968,7 @@ impl EmitterWriter { max_line_num_len, true) { Err(e) => panic!("failed to emit error: {}", e), - _ => () + _ => (), } } } diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 09a0c7f9be4e..bcae7b262c62 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -15,7 +15,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(custom_attribute)] #![allow(unused_attributes)] diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs index d02e4f9b165d..ea0a0034c3e9 100644 --- a/src/librustc_incremental/lib.rs +++ b/src/librustc_incremental/lib.rs @@ -17,7 +17,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(rustc_private)] #![feature(staged_api)] diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index c021ffee8189..8d86e7e2e8b9 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -138,7 +138,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxPointers { } fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { - let ty = cx.tcx.tables().node_id_to_type(e.id); + let ty = cx.tables.node_id_to_type(e.id); self.check_heap_type(cx, e.span, ty); } } @@ -738,8 +738,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion { // is this a recursive call? let self_recursive = if node_id != ast::DUMMY_NODE_ID { match method { - Some(ref method) => expr_refers_to_this_method(cx.tcx, method, node_id), - None => expr_refers_to_this_fn(cx.tcx, id, node_id), + Some(ref method) => expr_refers_to_this_method(cx, method, node_id), + None => expr_refers_to_this_fn(cx, id, node_id), } } else { false @@ -787,43 +787,42 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion { // Functions for identifying if the given Expr NodeId `id` // represents a call to the function `fn_id`/method `method`. - fn expr_refers_to_this_fn(tcx: TyCtxt, fn_id: ast::NodeId, id: ast::NodeId) -> bool { - match tcx.map.get(id) { + fn expr_refers_to_this_fn(cx: &LateContext, fn_id: ast::NodeId, id: ast::NodeId) -> bool { + match cx.tcx.map.get(id) { hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => { let def = if let hir::ExprPath(ref qpath) = callee.node { - tcx.tables().qpath_def(qpath, callee.id) + cx.tables.qpath_def(qpath, callee.id) } else { return false; }; - def.def_id() == tcx.map.local_def_id(fn_id) + def.def_id() == cx.tcx.map.local_def_id(fn_id) } _ => false, } } // Check if the expression `id` performs a call to `method`. - fn expr_refers_to_this_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - method: &ty::AssociatedItem, - id: ast::NodeId) - -> bool { + fn expr_refers_to_this_method(cx: &LateContext, + method: &ty::AssociatedItem, + id: ast::NodeId) + -> bool { use rustc::ty::adjustment::*; // Check for method calls and overloaded operators. - let opt_m = tcx.tables().method_map.get(&ty::MethodCall::expr(id)).cloned(); + let opt_m = cx.tables.method_map.get(&ty::MethodCall::expr(id)).cloned(); if let Some(m) = opt_m { - if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) { + if method_call_refers_to_method(cx.tcx, method, m.def_id, m.substs, id) { return true; } } // Check for overloaded autoderef method calls. - let opt_adj = tcx.tables().adjustments.get(&id).cloned(); + let opt_adj = cx.tables.adjustments.get(&id).cloned(); if let Some(Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) = opt_adj { for i in 0..autoderefs { let method_call = ty::MethodCall::autoderef(id, i as u32); - if let Some(m) = tcx.tables().method_map.get(&method_call) - .cloned() { - if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) { + if let Some(m) = cx.tables.method_map.get(&method_call).cloned() { + if method_call_refers_to_method(cx.tcx, method, m.def_id, m.substs, id) { return true; } } @@ -831,19 +830,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion { } // Check for calls to methods via explicit paths (e.g. `T::method()`). - match tcx.map.get(id) { + match cx.tcx.map.get(id) { hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => { let def = if let hir::ExprPath(ref qpath) = callee.node { - tcx.tables().qpath_def(qpath, callee.id) + cx.tables.qpath_def(qpath, callee.id) } else { return false; }; match def { Def::Method(def_id) => { - let substs = tcx.tables().node_id_item_substs(callee.id) - .unwrap_or_else(|| tcx.intern_substs(&[])); + let substs = cx.tables.node_id_item_substs(callee.id) + .unwrap_or_else(|| cx.tcx.intern_substs(&[])); method_call_refers_to_method( - tcx, method, def_id, substs, id) + cx.tcx, method, def_id, substs, id) } _ => false, } @@ -882,8 +881,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion { // checking, so it's always local let node_id = tcx.map.as_local_node_id(method.def_id).unwrap(); - let param_env = Some(ty::ParameterEnvironment::for_item(tcx, node_id)); - tcx.infer_ctxt(None, param_env, Reveal::NotSpecializable).enter(|infcx| { + let param_env = ty::ParameterEnvironment::for_item(tcx, node_id); + tcx.infer_ctxt(param_env, Reveal::NotSpecializable).enter(|infcx| { let mut selcx = traits::SelectionContext::new(&infcx); match selcx.select(&obligation) { // The method comes from a `T: Trait` bound. @@ -1073,7 +1072,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes { expr: &hir::Expr) -> Option<(&'tcx ty::TypeVariants<'tcx>, &'tcx ty::TypeVariants<'tcx>)> { let def = if let hir::ExprPath(ref qpath) = expr.node { - cx.tcx.tables().qpath_def(qpath, expr.id) + cx.tables.qpath_def(qpath, expr.id) } else { return None; }; @@ -1081,7 +1080,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes { if !def_id_is_transmute(cx, did) { return None; } - let typ = cx.tcx.tables().node_id_to_type(expr.id); + let typ = cx.tables.node_id_to_type(expr.id); match typ.sty { ty::TyFnDef(.., ref bare_fn) if bare_fn.abi == RustIntrinsic => { let from = bare_fn.sig.skip_binder().inputs()[0]; diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index a24edfaaac1c..60497fe171cc 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -26,7 +26,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![cfg_attr(test, feature(test))] #![feature(box_patterns)] @@ -165,6 +165,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { DEAD_CODE, UNUSED_MUT, UNREACHABLE_CODE, + UNREACHABLE_PATTERNS, UNUSED_MUST_USE, UNUSED_UNSAFE, PATH_STATEMENTS, diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 95e955bd6833..2a77e9a4a7c8 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -16,7 +16,7 @@ use rustc::ty::{self, AdtKind, Ty, TyCtxt}; use rustc::ty::layout::{Layout, Primitive}; use rustc::traits::Reveal; use middle::const_val::ConstVal; -use rustc_const_eval::eval_const_expr_partial; +use rustc_const_eval::ConstContext; use rustc_const_eval::EvalHint::ExprTypeChecked; use util::nodemap::FxHashSet; use lint::{LateContext, LintContext, LintArray}; @@ -35,32 +35,6 @@ use rustc::hir; use rustc_i128::{i128, u128}; -register_long_diagnostics! { -E0519: r##" -It is not allowed to negate an unsigned integer. -You can negate a signed integer and cast it to an -unsigned integer or use the `!` operator. - -``` -let x: usize = -1isize as usize; -let y: usize = !0; -assert_eq!(x, y); -``` - -Alternatively you can use the `Wrapping` newtype -or the `wrapping_neg` operation that all -integral types support: - -``` -use std::num::Wrapping; -let x: Wrapping = -Wrapping(1); -let Wrapping(x) = x; -let y: usize = 1.wrapping_neg(); -assert_eq!(x, y); -``` -"## -} - declare_lint! { UNUSED_COMPARISONS, Warn, @@ -109,38 +83,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { match e.node { hir::ExprUnary(hir::UnNeg, ref expr) => { - if let hir::ExprLit(ref lit) = expr.node { - match lit.node { - ast::LitKind::Int(_, ast::LitIntType::Unsigned(_)) => { - forbid_unsigned_negation(cx, e.span); - } - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { - if let ty::TyUint(_) = cx.tcx.tables().node_id_to_type(e.id).sty { - forbid_unsigned_negation(cx, e.span); - } - } - _ => (), - } - } else { - let t = cx.tcx.tables().node_id_to_type(expr.id); - if let ty::TyUint(_) = t.sty { - forbid_unsigned_negation(cx, e.span); - } - } // propagate negation, if the negation itself isn't negated if self.negated_expr_id != e.id { self.negated_expr_id = expr.id; } } hir::ExprBinary(binop, ref l, ref r) => { - if is_comparison(binop) && !check_limits(cx.tcx, binop, &l, &r) { + if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { cx.span_lint(UNUSED_COMPARISONS, e.span, "comparison is useless due to type limits"); } if binop.node.is_shift() { - let opt_ty_bits = match cx.tcx.tables().node_id_to_type(l.id).sty { + let opt_ty_bits = match cx.tables.node_id_to_type(l.id).sty { ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.int_type)), ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)), _ => None, @@ -154,7 +110,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { false } } else { - match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked, None) { + let const_cx = ConstContext::with_tables(cx.tcx, cx.tables); + match const_cx.eval(&r, ExprTypeChecked) { Ok(ConstVal::Integral(i)) => { i.is_negative() || i.to_u64() @@ -173,7 +130,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } } hir::ExprLit(ref lit) => { - match cx.tcx.tables().node_id_to_type(e.id).sty { + match cx.tables.node_id_to_type(e.id).sty { ty::TyInt(t) => { match lit.node { ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | @@ -318,11 +275,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } } - fn check_limits<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - binop: hir::BinOp, - l: &hir::Expr, - r: &hir::Expr) - -> bool { + fn check_limits(cx: &LateContext, + binop: hir::BinOp, + l: &hir::Expr, + r: &hir::Expr) + -> bool { let (lit, expr, swap) = match (&l.node, &r.node) { (&hir::ExprLit(_), _) => (l, r, true), (_, &hir::ExprLit(_)) => (r, l, false), @@ -331,7 +288,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { // Normalize the binop so that the literal is always on the RHS in // the comparison let norm_binop = if swap { rev_binop(binop) } else { binop }; - match tcx.tables().node_id_to_type(expr.id).sty { + match cx.tables.node_id_to_type(expr.id).sty { ty::TyInt(int_ty) => { let (min, max) = int_ty_range(int_ty); let lit_val: i128 = match lit.node { @@ -369,13 +326,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { _ => false, } } - - fn forbid_unsigned_negation(cx: &LateContext, span: Span) { - cx.sess() - .struct_span_err_with_code(span, "unary negation of unsigned integer", "E0519") - .span_help(span, "use a cast or the `!` operator") - .emit(); - } } } @@ -747,7 +697,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { if gens.ty_params.is_empty() { // sizes only make sense for non-generic types let t = cx.tcx.item_type(cx.tcx.map.local_def_id(it.id)); - let layout = cx.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + let layout = cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let ty = cx.tcx.erase_regions(&t); ty.layout(&infcx).unwrap_or_else(|e| { bug!("failed to get layout for `{}`: {}", t, e) diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index b7ee688117d9..48d9f5e72c26 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -139,7 +139,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { return; } - let t = cx.tcx.tables().expr_ty(&expr); + let t = cx.tables.expr_ty(&expr); let warned = match t.sty { ty::TyTuple(ref tys) if tys.is_empty() => return, ty::TyNever => return, @@ -440,7 +440,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation { _ => return, } - if let Some(adjustment) = cx.tcx.tables().adjustments.get(&e.id) { + if let Some(adjustment) = cx.tables.adjustments.get(&e.id) { if let adjustment::Adjust::DerefRef { autoref, .. } = adjustment.kind { match autoref { Some(adjustment::AutoBorrow::Ref(_, hir::MutImmutable)) => { diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 938f346e0bf8..b1615b9e38bd 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -20,7 +20,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(associated_consts)] #![feature(box_syntax)] diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index b27b164bd47a..3c14d38cc382 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -8,59 +8,31 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::hir::map as ast_map; +use rustc::hir::intravisit::{Visitor, NestedVisitorMap}; -use rustc::hir::intravisit::{Visitor, IdRangeComputingVisitor, IdRange, NestedVisitorMap}; - -use cstore::CrateMetadata; use encoder::EncodeContext; use schema::*; use rustc::hir; -use rustc::hir::def::Def; -use rustc::hir::def_id::DefId; -use rustc::ty::{self, TyCtxt, Ty}; - -use syntax::ast; +use rustc::ty; use rustc_serialize::Encodable; #[derive(RustcEncodable, RustcDecodable)] pub struct Ast<'tcx> { - id_range: IdRange, - body: Lazy, - side_tables: LazySeq<(ast::NodeId, TableEntry<'tcx>)>, + pub body: Lazy, + pub tables: Lazy>, pub nested_bodies: LazySeq, pub rvalue_promotable_to_static: bool, } -#[derive(RustcEncodable, RustcDecodable)] -enum TableEntry<'tcx> { - TypeRelativeDef(Def), - NodeType(Ty<'tcx>), - ItemSubsts(ty::ItemSubsts<'tcx>), - Adjustment(ty::adjustment::Adjustment<'tcx>), -} - impl<'a, 'tcx> EncodeContext<'a, 'tcx> { - pub fn encode_body(&mut self, body: hir::BodyId) -> Lazy> { - let body = self.tcx.map.body(body); + pub fn encode_body(&mut self, body_id: hir::BodyId) -> Lazy> { + let body = self.tcx.map.body(body_id); + let lazy_body = self.lazy(body); - let mut id_visitor = IdRangeComputingVisitor::new(&self.tcx.map); - id_visitor.visit_body(body); - - let body_pos = self.position(); - body.encode(self).unwrap(); - - let tables_pos = self.position(); - let tables_count = { - let mut visitor = SideTableEncodingIdVisitor { - ecx: self, - count: 0, - }; - visitor.visit_body(body); - visitor.count - }; + let tables = self.tcx.body_tables(body_id); + let lazy_tables = self.lazy(tables); let nested_pos = self.position(); let nested_count = { @@ -76,44 +48,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tcx.rvalue_promotable_to_static.borrow()[&body.value.id]; self.lazy(&Ast { - id_range: id_visitor.result(), - body: Lazy::with_position(body_pos), - side_tables: LazySeq::with_position_and_length(tables_pos, tables_count), + body: lazy_body, + tables: lazy_tables, nested_bodies: LazySeq::with_position_and_length(nested_pos, nested_count), rvalue_promotable_to_static: rvalue_promotable_to_static }) } } -struct SideTableEncodingIdVisitor<'a, 'b: 'a, 'tcx: 'b> { - ecx: &'a mut EncodeContext<'b, 'tcx>, - count: usize, -} - -impl<'a, 'b, 'tcx> Visitor<'tcx> for SideTableEncodingIdVisitor<'a, 'b, 'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.ecx.tcx.map) - } - - fn visit_id(&mut self, id: ast::NodeId) { - debug!("Encoding side tables for id {}", id); - - let tcx = self.ecx.tcx; - let mut encode = |entry: Option| { - if let Some(entry) = entry { - (id, entry).encode(self.ecx).unwrap(); - self.count += 1; - } - }; - - encode(tcx.tables().type_relative_path_defs.get(&id).cloned() - .map(TableEntry::TypeRelativeDef)); - encode(tcx.tables().node_types.get(&id).cloned().map(TableEntry::NodeType)); - encode(tcx.tables().item_substs.get(&id).cloned().map(TableEntry::ItemSubsts)); - encode(tcx.tables().adjustments.get(&id).cloned().map(TableEntry::Adjustment)); - } -} - struct NestedBodyEncodingVisitor<'a, 'b: 'a, 'tcx: 'b> { ecx: &'a mut EncodeContext<'b, 'tcx>, count: usize, @@ -132,41 +74,3 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedBodyEncodingVisitor<'a, 'b, 'tcx> { self.visit_body(body); } } - -/// Decodes an item's body from its AST in the cdata's metadata and adds it to the -/// ast-map. -pub fn decode_body<'a, 'tcx>(cdata: &CrateMetadata, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - ast: Ast<'tcx>) - -> &'tcx hir::Body { - debug!("> Decoding inlined fn: {}", tcx.item_path_str(def_id)); - - let cnt = ast.id_range.max.as_usize() - ast.id_range.min.as_usize(); - let start = tcx.sess.reserve_node_ids(cnt); - let id_ranges = [ast.id_range, - IdRange { - min: start, - max: ast::NodeId::new(start.as_usize() + cnt), - }]; - - for (id, entry) in ast.side_tables.decode((cdata, tcx, id_ranges)) { - match entry { - TableEntry::TypeRelativeDef(def) => { - tcx.tables.borrow_mut().type_relative_path_defs.insert(id, def); - } - TableEntry::NodeType(ty) => { - tcx.tables.borrow_mut().node_types.insert(id, ty); - } - TableEntry::ItemSubsts(item_substs) => { - tcx.tables.borrow_mut().item_substs.insert(id, item_substs); - } - TableEntry::Adjustment(adj) => { - tcx.tables.borrow_mut().adjustments.insert(id, adj); - } - } - } - - let body = ast.body.decode((cdata, tcx, id_ranges)); - ast_map::map_decoded_body(&tcx.map, def_id, body, tcx.sess.next_node_id()) -} diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 724c164b3b41..8f7b9c24cbf8 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -302,10 +302,13 @@ impl<'a> CrateLoader<'a> { crate_root.def_path_table.decode(&metadata) }); + let exported_symbols = crate_root.exported_symbols.decode(&metadata).collect(); + let mut cmeta = cstore::CrateMetadata { name: name, extern_crate: Cell::new(None), def_path_table: def_path_table, + exported_symbols: exported_symbols, proc_macros: crate_root.macro_derive_registrar.map(|_| { self.load_derive_macros(&crate_root, dylib.clone().map(|p| p.0), span) }), diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index aab4034b7705..761041ad7198 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -80,6 +80,8 @@ pub struct CrateMetadata { /// compilation support. pub def_path_table: DefPathTable, + pub exported_symbols: FxHashSet, + pub dep_kind: Cell, pub source: CrateSource, @@ -97,7 +99,6 @@ pub struct CStore { used_link_args: RefCell>, statically_included_foreign_items: RefCell>, pub dllimport_foreign_items: RefCell>, - pub inlined_item_cache: RefCell>>, pub visible_parent_map: RefCell>, } @@ -112,7 +113,6 @@ impl CStore { statically_included_foreign_items: RefCell::new(FxHashSet()), dllimport_foreign_items: RefCell::new(FxHashSet()), visible_parent_map: RefCell::new(FxHashMap()), - inlined_item_cache: RefCell::new(FxHashMap()), } } diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 64513fa41b21..7cd26df0246e 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -226,6 +226,10 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { self.do_is_statically_included_foreign_item(def_id) } + fn is_exported_symbol(&self, def_id: DefId) -> bool { + self.get_crate_data(def_id.krate).exported_symbols.contains(&def_id.index) + } + fn is_dllimport_foreign_item(&self, def_id: DefId) -> bool { if def_id.krate == LOCAL_CRATE { self.dllimport_foreign_items.borrow().contains(&def_id.index) @@ -434,27 +438,14 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { def_id: DefId) -> Option<&'tcx hir::Body> { - self.dep_graph.read(DepNode::MetaData(def_id)); - - if let Some(&cached) = self.inlined_item_cache.borrow().get(&def_id) { - return cached.map(|root_id| { - // Already inline - debug!("maybe_get_item_body({}): already inline", tcx.item_path_str(def_id)); - tcx.map.expect_inlined_body(root_id) - }); + if let Some(cached) = tcx.map.get_inlined_body(def_id) { + return Some(cached); } + self.dep_graph.read(DepNode::MetaData(def_id)); debug!("maybe_get_item_body({}): inlining item", tcx.item_path_str(def_id)); - let inlined = self.get_crate_data(def_id.krate).maybe_get_item_body(tcx, def_id.index); - - self.inlined_item_cache.borrow_mut().insert(def_id, inlined.map(|body| { - let root_id = tcx.map.get_parent_node(body.value.id); - assert_eq!(tcx.map.get_parent_node(root_id), root_id); - root_id - })); - - inlined + self.get_crate_data(def_id.krate).maybe_get_item_body(tcx, def_id.index) } fn item_body_nested_bodies(&self, def: DefId) -> BTreeMap { @@ -479,11 +470,6 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { self.get_crate_data(def.krate).is_item_mir_available(def.index) } - fn can_have_local_instance<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> bool { - self.dep_graph.read(DepNode::MetaData(def)); - def.is_local() || self.get_crate_data(def.krate).can_have_local_instance(tcx, def.index) - } - fn crates(&self) -> Vec { let mut result = vec![]; diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index dad956afb5e6..4abdee345c29 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -10,13 +10,11 @@ // Decoding metadata from a single crate's metadata -use astencode::decode_body; use cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary}; use schema::*; use rustc::hir::map::{DefKey, DefPath, DefPathData}; use rustc::hir; -use rustc::hir::intravisit::IdRange; use rustc::middle::cstore::LinkagePreference; use rustc::hir::def::{self, Def, CtorKind}; @@ -40,7 +38,7 @@ use std::u32; use rustc_serialize::{Decodable, Decoder, SpecializedDecoder, opaque}; use syntax::attr; -use syntax::ast::{self, NodeId}; +use syntax::ast; use syntax::codemap; use syntax_pos::{self, Span, BytePos, Pos, DUMMY_SP}; use rustc_i128::{u128, i128}; @@ -50,8 +48,6 @@ pub struct DecodeContext<'a, 'tcx: 'a> { cdata: Option<&'a CrateMetadata>, sess: Option<&'a Session>, tcx: Option>, - from_id_range: IdRange, - to_id_range: IdRange, // Cache the last used filemap for translating spans as an optimization. last_filemap_index: usize, @@ -67,18 +63,12 @@ pub trait Metadata<'a, 'tcx>: Copy { fn tcx(self) -> Option> { None } fn decoder(self, pos: usize) -> DecodeContext<'a, 'tcx> { - let id_range = IdRange { - min: NodeId::from_u32(u32::MIN), - max: NodeId::from_u32(u32::MAX), - }; let tcx = self.tcx(); DecodeContext { opaque: opaque::Decoder::new(self.raw_bytes(), pos), cdata: self.cdata(), sess: self.sess().or(tcx.map(|tcx| tcx.sess)), tcx: tcx, - from_id_range: id_range, - to_id_range: id_range, last_filemap_index: 0, lazy_state: LazyState::NoNode, } @@ -128,26 +118,6 @@ impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadata, TyCtxt<'a, 'tcx, 'tcx> } } -// HACK(eddyb) Only used by astencode to customize the from/to IdRange's. -impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadata, TyCtxt<'a, 'tcx, 'tcx>, [IdRange; 2]) { - fn raw_bytes(self) -> &'a [u8] { - self.0.raw_bytes() - } - fn cdata(self) -> Option<&'a CrateMetadata> { - Some(self.0) - } - fn tcx(self) -> Option> { - Some(self.1) - } - - fn decoder(self, pos: usize) -> DecodeContext<'a, 'tcx> { - let mut dcx = (self.0, self.1).decoder(pos); - dcx.from_id_range = self.2[0]; - dcx.to_id_range = self.2[1]; - dcx - } -} - impl<'a, 'tcx: 'a, T: Decodable> Lazy { pub fn decode>(self, meta: M) -> T { let mut dcx = meta.decoder(self.position); @@ -256,28 +226,6 @@ impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> { } } -impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - let id = u32::decode(self)?; - - // from_id_range should be non-empty - assert!(!self.from_id_range.empty()); - // Make sure that translating the NodeId will actually yield a - // meaningful result - if !self.from_id_range.contains(NodeId::from_u32(id)) { - bug!("NodeId::decode: {} out of DecodeContext range ({:?} -> {:?})", - id, - self.from_id_range, - self.to_id_range); - } - - // Use wrapping arithmetic because otherwise it introduces control flow. - // Maybe we should just have the control flow? -- aatch - Ok(NodeId::from_u32(id.wrapping_sub(self.from_id_range.min.as_u32()) - .wrapping_add(self.to_id_range.min.as_u32()))) - } -} - impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { let cnum = CrateNum::from_u32(u32::decode(self)?); @@ -497,14 +445,6 @@ impl<'tcx> EntryKind<'tcx> { EntryKind::Closure(_) => return None, }) } - fn is_const_fn(&self, meta: &CrateMetadata) -> bool { - let constness = match *self { - EntryKind::Method(data) => data.decode(meta).fn_data.constness, - EntryKind::Fn(data) => data.decode(meta).constness, - _ => hir::Constness::NotConst, - }; - constness == hir::Constness::Const - } } impl<'a, 'tcx> CrateMetadata { @@ -829,7 +769,14 @@ impl<'a, 'tcx> CrateMetadata { -> Option<&'tcx hir::Body> { if self.is_proc_macro(id) { return None; } self.entry(id).ast.map(|ast| { - decode_body(self, tcx, self.local_def_id(id), ast.decode(self)) + let def_id = self.local_def_id(id); + let ast = ast.decode(self); + + let tables = ast.tables.decode((self, tcx)); + tcx.tables.borrow_mut().insert(def_id, tcx.alloc_tables(tables)); + + let body = ast.body.decode((self, tcx)); + tcx.map.intern_inlined_body(def_id, body) }) } @@ -849,29 +796,6 @@ impl<'a, 'tcx> CrateMetadata { self.maybe_entry(id).and_then(|item| item.decode(self).mir).is_some() } - pub fn can_have_local_instance(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - id: DefIndex) -> bool { - self.maybe_entry(id).map_or(false, |item| { - let item = item.decode(self); - // if we don't have a MIR, then this item was never meant to be locally instantiated - // or we have a bug in the metadata serialization - item.mir.is_some() && ( - // items with generics always can have local instances if monomorphized - item.generics.map_or(false, |generics| { - let generics = generics.decode((self, tcx)); - generics.parent_types != 0 || !generics.types.is_empty() - }) || - match item.kind { - EntryKind::Closure(_) => true, - _ => false, - } || - item.kind.is_const_fn(self) || - attr::requests_inline(&self.get_attributes(&item)) - ) - }) - } - pub fn maybe_get_item_mir(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefIndex) @@ -1076,7 +1000,7 @@ impl<'a, 'tcx> CrateMetadata { } pub fn get_exported_symbols(&self) -> Vec { - self.root.exported_symbols.decode(self).map(|index| self.local_def_id(index)).collect() + self.exported_symbols.iter().map(|&index| self.local_def_id(index)).collect() } pub fn get_macro(&self, id: DefIndex) -> (ast::Name, MacroDef) { @@ -1088,7 +1012,12 @@ impl<'a, 'tcx> CrateMetadata { } pub fn is_const_fn(&self, id: DefIndex) -> bool { - self.entry(id).kind.is_const_fn(self) + let constness = match self.entry(id).kind { + EntryKind::Method(data) => data.decode(self).fn_data.constness, + EntryKind::Fn(data) => data.decode(self).constness, + _ => hir::Constness::NotConst, + }; + constness == hir::Constness::Const } pub fn is_foreign_item(&self, id: DefIndex) -> bool { diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index c3bcdf42d4ed..7de768b7b904 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -1037,7 +1037,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let data = ClosureData { kind: tcx.closure_kind(def_id), - ty: self.lazy(&tcx.tables().closure_tys[&def_id]), + ty: self.lazy(&tcx.closure_tys.borrow()[&def_id]), }; Entry { diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index f4ccc01544aa..484e0df65830 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -15,13 +15,12 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(box_patterns)] #![feature(conservative_impl_trait)] #![feature(core_intrinsics)] #![feature(proc_macro_internals)] -#![feature(proc_macro_lib)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 71282dcf0ba0..b07183412236 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -26,6 +26,7 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use build::matches::{Binding, MatchPair, Candidate}; use hair::*; use rustc::mir::*; +use rustc_data_structures::fx::FxHashSet; use std::mem; @@ -93,11 +94,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } PatternKind::Range { .. } | - PatternKind::Variant { .. } | PatternKind::Slice { .. } => { Err(match_pair) } + PatternKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { + let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| { + i == variant_index || { + let mut visited = FxHashSet::default(); + let node_set = v.uninhabited_from(&mut visited, + self.hir.tcx(), + substs, + adt_def.adt_kind()); + !node_set.is_empty() + } + }); + if irrefutable { + let lvalue = match_pair.lvalue.downcast(adt_def, variant_index); + candidate.match_pairs.extend(self.field_match_pairs(lvalue, subpatterns)); + Ok(()) + } else { + Err(match_pair) + } + }, + PatternKind::Array { ref prefix, ref slice, ref suffix } => { self.prefix_slice_suffix(&mut candidate.match_pairs, &match_pair.lvalue, diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index cb449037aeba..8b4a013bad0a 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -32,7 +32,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// It is a bug to call this with a simplifyable pattern. pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { match *match_pair.pattern.kind { - PatternKind::Variant { ref adt_def, variant_index: _, subpatterns: _ } => { + PatternKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => { Test { span: match_pair.pattern.span, kind: TestKind::Switch { @@ -451,7 +451,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // If we are performing a variant switch, then this // informs variant patterns, but nothing else. (&TestKind::Switch { adt_def: tested_adt_def, .. }, - &PatternKind::Variant { adt_def, variant_index, ref subpatterns }) => { + &PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. }) => { assert_eq!(adt_def, tested_adt_def); let new_candidate = self.candidate_after_variant_switch(match_pair_index, diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index cfdc1bf27df3..7347841a5f1e 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -169,7 +169,7 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| { freevars.iter().map(|fv| { let var_id = tcx.map.as_local_node_id(fv.def.def_id()).unwrap(); - let by_ref = tcx.tables().upvar_capture(ty::UpvarId { + let by_ref = hir.tables().upvar_capture(ty::UpvarId { var_id: var_id, closure_expr_id: fn_id }).map_or(false, |capture| match capture { @@ -195,13 +195,12 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, } pub fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, - item_id: ast::NodeId, body_id: hir::BodyId) -> Mir<'tcx> { let tcx = hir.tcx(); let ast_expr = &tcx.map.body(body_id).value; - let ty = tcx.tables().expr_ty_adjusted(ast_expr); - let span = tcx.map.span(item_id); + let ty = hir.tables().expr_ty_adjusted(ast_expr); + let span = tcx.map.span(tcx.map.body_owner(body_id)); let mut builder = Builder::new(hir, span, 0, ty); let extent = tcx.region_maps.temporary_scope(ast_expr.id) @@ -306,7 +305,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let lvalue = Lvalue::Local(Local::new(index + 1)); if let Some(pattern) = pattern { - let pattern = Pattern::from_hir(self.hir.tcx(), pattern); + let pattern = Pattern::from_hir(self.hir.tcx(), self.hir.tables(), pattern); scope = self.declare_bindings(scope, ast_body.span, &pattern); unpack!(block = self.lvalue_into_pattern(block, pattern, &lvalue)); } diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index b355c8f2c4c6..4b3d62fd6d6e 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -61,7 +61,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let remainder_extent = cx.tcx.region_maps.lookup_code_extent(remainder_extent); - let pattern = Pattern::from_hir(cx.tcx, &local.pat); + let pattern = Pattern::from_hir(cx.tcx, cx.tables(), &local.pat); result.push(StmtRef::Mirror(Box::new(Stmt { span: stmt.span, kind: StmtKind::Let { @@ -82,7 +82,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, pub fn to_expr_ref<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, block: &'tcx hir::Block) -> ExprRef<'tcx> { - let block_ty = cx.tcx.tables().node_id_to_type(block.id); + let block_ty = cx.tables().node_id_to_type(block.id); let temp_lifetime = cx.tcx.region_maps.temporary_scope(block.id); let expr = Expr { ty: block_ty, diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index cc65fdede092..fe10fb57c35e 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -17,7 +17,7 @@ use hair::cx::to_ref::ToRef; use rustc::hir::map; use rustc::hir::def::{Def, CtorKind}; use rustc::middle::const_val::ConstVal; -use rustc_const_eval as const_eval; +use rustc_const_eval::{ConstContext, EvalHint, fatal_const_eval_err}; use rustc::ty::{self, AdtKind, VariantDef, Ty}; use rustc::ty::cast::CastKind as TyCastKind; use rustc::hir; @@ -33,7 +33,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span); let mut expr = make_mirror_unadjusted(cx, self); - let adj = cx.tcx.tables().adjustments.get(&self.id).cloned(); + let adj = cx.tables().adjustments.get(&self.id).cloned(); debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}", expr, @@ -80,13 +80,13 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { let i = i as u32; let adjusted_ty = expr.ty.adjust_for_autoderef(cx.tcx, self.id, self.span, i, |mc| { - cx.tcx.tables().method_map.get(&mc).map(|m| m.ty) + cx.tables().method_map.get(&mc).map(|m| m.ty) }); debug!("make_mirror: autoderef #{}, adjusted_ty={:?}", i, adjusted_ty); let method_key = ty::MethodCall::autoderef(self.id, i); - let meth_ty = cx.tcx.tables().method_map.get(&method_key).map(|m| m.ty); + let meth_ty = cx.tables().method_map.get(&method_key).map(|m| m.ty); let kind = if let Some(meth_ty) = meth_ty { debug!("make_mirror: overloaded autoderef (meth_ty={:?})", meth_ty); @@ -217,7 +217,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, expr: &'tcx hir::Expr) -> Expr<'tcx> { - let expr_ty = cx.tcx.tables().expr_ty(expr); + let expr_ty = cx.tables().expr_ty(expr); let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id); let kind = match expr.node { @@ -236,7 +236,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } hir::ExprCall(ref fun, ref args) => { - if cx.tcx.tables().is_method_call(expr.id) { + if cx.tables().is_method_call(expr.id) { // The callee is something implementing Fn, FnMut, or FnOnce. // Find the actual method implementation being called and // build the appropriate UFCS call expression with the @@ -285,9 +285,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, None }; if let Some((adt_def, index)) = adt_data { - let substs = cx.tcx - .tables() - .node_id_item_substs(fun.id) + let substs = cx.tables().node_id_item_substs(fun.id) .unwrap_or_else(|| cx.tcx.intern_substs(&[])); let field_refs = args.iter() .enumerate() @@ -307,7 +305,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } else { ExprKind::Call { - ty: cx.tcx.tables().node_id_to_type(fun.id), + ty: cx.tables().node_id_to_type(fun.id), fun: fun.to_ref(), args: args.to_ref(), } @@ -337,7 +335,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } hir::ExprAssignOp(op, ref lhs, ref rhs) => { - if cx.tcx.tables().is_method_call(expr.id) { + if cx.tables().is_method_call(expr.id) { let pass_args = if op.node.is_by_value() { PassArgs::ByValue } else { @@ -361,7 +359,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, hir::ExprLit(..) => ExprKind::Literal { literal: cx.const_eval_literal(expr) }, hir::ExprBinary(op, ref lhs, ref rhs) => { - if cx.tcx.tables().is_method_call(expr.id) { + if cx.tables().is_method_call(expr.id) { let pass_args = if op.node.is_by_value() { PassArgs::ByValue } else { @@ -421,7 +419,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } hir::ExprIndex(ref lhs, ref index) => { - if cx.tcx.tables().is_method_call(expr.id) { + if cx.tables().is_method_call(expr.id) { overloaded_lvalue(cx, expr, ty::MethodCall::expr(expr.id), @@ -437,7 +435,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => { - if cx.tcx.tables().is_method_call(expr.id) { + if cx.tables().is_method_call(expr.id) { overloaded_lvalue(cx, expr, ty::MethodCall::expr(expr.id), @@ -450,7 +448,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } hir::ExprUnary(hir::UnOp::UnNot, ref arg) => { - if cx.tcx.tables().is_method_call(expr.id) { + if cx.tables().is_method_call(expr.id) { overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), @@ -466,7 +464,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => { - if cx.tcx.tables().is_method_call(expr.id) { + if cx.tables().is_method_call(expr.id) { overloaded_operator(cx, expr, ty::MethodCall::expr(expr.id), @@ -500,8 +498,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, base: base.as_ref().map(|base| { FruInfo { base: base.to_ref(), - field_types: cx.tcx.tables().fru_field_types[&expr.id] - .clone(), + field_types: cx.tables().fru_field_types[&expr.id].clone(), } }), } @@ -541,7 +538,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } hir::ExprClosure(..) => { - let closure_ty = cx.tcx.tables().expr_ty(expr); + let closure_ty = cx.tables().expr_ty(expr); let (def_id, substs) = match closure_ty.sty { ty::TyClosure(def_id, substs) => (def_id, substs), _ => { @@ -562,7 +559,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } hir::ExprPath(ref qpath) => { - let def = cx.tcx.tables().qpath_def(qpath, expr.id); + let def = cx.tables().qpath_def(qpath, expr.id); convert_path_expr(cx, expr, def) } @@ -575,17 +572,21 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } // Now comes the rote stuff: - hir::ExprRepeat(ref v, c) => { - let c = &cx.tcx.map.body(c).value; + hir::ExprRepeat(ref v, count) => { + let tcx = cx.tcx.global_tcx(); + let c = &cx.tcx.map.body(count).value; + let count = match ConstContext::new(tcx, count).eval(c, EvalHint::ExprTypeChecked) { + Ok(ConstVal::Integral(ConstInt::Usize(u))) => u, + Ok(other) => bug!("constant evaluation of repeat count yielded {:?}", other), + Err(s) => fatal_const_eval_err(tcx, &s, c.span, "expression") + }; + ExprKind::Repeat { value: v.to_ref(), count: TypedConstVal { - ty: cx.tcx.tables().expr_ty(c), + ty: cx.tcx.types.usize, span: c.span, - value: match const_eval::eval_const_expr(cx.tcx.global_tcx(), c) { - ConstVal::Integral(ConstInt::Usize(u)) => u, - other => bug!("constant evaluation of repeat count yielded {:?}", other), - }, + value: count } } } @@ -627,7 +628,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } hir::ExprField(ref source, name) => { - let index = match cx.tcx.tables().expr_ty_adjusted(source).sty { + let index = match cx.tables().expr_ty_adjusted(source).sty { ty::TyAdt(adt_def, _) => adt_def.variants[0].index_of_field_named(name.node), ref ty => span_bug!(expr.span, "field of non-ADT: {:?}", ty), }; @@ -679,7 +680,7 @@ fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, expr: &hir::Expr, method_call: ty::MethodCall) -> Expr<'tcx> { - let callee = cx.tcx.tables().method_map[&method_call]; + let callee = cx.tables().method_map[&method_call]; let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id); Expr { temp_lifetime: temp_lifetime, @@ -703,7 +704,7 @@ fn to_borrow_kind(m: hir::Mutability) -> BorrowKind { fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm) -> Arm<'tcx> { Arm { - patterns: arm.pats.iter().map(|p| Pattern::from_hir(cx.tcx, p)).collect(), + patterns: arm.pats.iter().map(|p| Pattern::from_hir(cx.tcx, cx.tables(), p)).collect(), guard: arm.guard.to_ref(), body: arm.body.to_ref(), } @@ -713,9 +714,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, expr: &'tcx hir::Expr, def: Def) -> ExprKind<'tcx> { - let substs = cx.tcx - .tables() - .node_id_item_substs(expr.id) + let substs = cx.tables().node_id_item_substs(expr.id) .unwrap_or_else(|| cx.tcx.intern_substs(&[])); let def_id = match def { // A regular function, constructor function or a constant. @@ -728,7 +727,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, Def::StructCtor(def_id, CtorKind::Const) | Def::VariantCtor(def_id, CtorKind::Const) => { - match cx.tcx.tables().node_id_to_type(expr.id).sty { + match cx.tables().node_id_to_type(expr.id).sty { // A unit struct/variant which is used as a value. // We return a completely different ExprKind here to account for this special case. ty::TyAdt(adt_def, substs) => { @@ -776,7 +775,7 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, id_var, index, closure_expr_id); - let var_ty = cx.tcx.tables().node_id_to_type(id_var); + let var_ty = cx.tables().node_id_to_type(id_var); let body_id = match cx.tcx.map.find(closure_expr_id) { Some(map::NodeExpr(expr)) => { @@ -793,7 +792,7 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, }; // FIXME free regions in closures are not right - let closure_ty = cx.tcx.tables().node_id_to_type(closure_expr_id); + let closure_ty = cx.tables().node_id_to_type(closure_expr_id); // FIXME we're just hard-coding the idea that the // signature will be &self or &mut self and hence will @@ -869,7 +868,7 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, var_id: id_var, closure_expr_id: closure_expr_id, }; - let upvar_capture = match cx.tcx.tables().upvar_capture(upvar_id) { + let upvar_capture = match cx.tables().upvar_capture(upvar_id) { Some(c) => c, None => { span_bug!(expr.span, "no upvar_capture for {:?}", upvar_id); @@ -948,7 +947,7 @@ fn overloaded_operator<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id); argrefs.extend(args.iter() .map(|arg| { - let arg_ty = cx.tcx.tables().expr_ty_adjusted(arg); + let arg_ty = cx.tables().expr_ty_adjusted(arg); let adjusted_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: arg_ty, @@ -990,7 +989,7 @@ fn overloaded_lvalue<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, // line up (this is because `*x` and `x[y]` represent lvalues): // to find the type &T of the content returned by the method; - let ref_ty = cx.tcx.tables().method_map[&method_call].ty.fn_ret(); + let ref_ty = cx.tables().method_map[&method_call].ty.fn_ret(); let ref_ty = cx.tcx.no_late_bound_regions(&ref_ty).unwrap(); // callees always have all late-bound regions fully instantiated, @@ -1019,9 +1018,9 @@ fn capture_freevar<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, var_id: id_var, closure_expr_id: closure_expr.id, }; - let upvar_capture = cx.tcx.tables().upvar_capture(upvar_id).unwrap(); + let upvar_capture = cx.tables().upvar_capture(upvar_id).unwrap(); let temp_lifetime = cx.tcx.region_maps.temporary_scope(closure_expr.id); - let var_ty = cx.tcx.tables().node_id_to_type(id_var); + let var_ty = cx.tables().node_id_to_type(id_var); let captured_var = Expr { temp_lifetime: temp_lifetime, ty: var_ty, diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 7d111fccd005..4b553a71b832 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -18,9 +18,8 @@ use hair::*; use rustc::mir::transform::MirSource; use rustc::middle::const_val::ConstVal; -use rustc_const_eval as const_eval; +use rustc_const_eval::{ConstContext, EvalHint, fatal_const_eval_err}; use rustc_data_structures::indexed_vec::Idx; -use rustc::dep_graph::DepNode; use rustc::hir::def_id::DefId; use rustc::hir::map::blocks::FnLikeNode; use rustc::infer::InferCtxt; @@ -52,17 +51,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { MirSource::Promoted(..) => bug!(), }; - let src_node_id = src.item_id(); - - // We are going to be accessing various tables - // generated by TypeckItemBody; we also assume - // that the body passes type check. These tables - // are not individually tracked, so just register - // a read here. - let src_def_id = infcx.tcx.map.local_def_id(src_node_id); - infcx.tcx.dep_graph.read(DepNode::TypeckItemBody(src_def_id)); - - let attrs = infcx.tcx.map.attrs(src_node_id); + let attrs = infcx.tcx.map.attrs(src.item_id()); // Some functions always have overflow checks enabled, // however, they may not get codegen'd, depending on @@ -128,7 +117,11 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { } pub fn const_eval_literal(&mut self, e: &hir::Expr) -> Literal<'tcx> { - Literal::Value { value: const_eval::eval_const_expr(self.tcx.global_tcx(), e) } + let tcx = self.tcx.global_tcx(); + match ConstContext::with_tables(tcx, self.tables()).eval(e, EvalHint::ExprTypeChecked) { + Ok(value) => Literal::Value { value: value }, + Err(s) => fatal_const_eval_err(tcx, &s, e.span, "expression") + } } pub fn trait_method(&mut self, @@ -177,6 +170,10 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { self.tcx } + pub fn tables(&self) -> &'a ty::Tables<'gcx> { + self.infcx.tables.expect_interned() + } + pub fn check_overflow(&self) -> bool { self.check_overflow } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index a608275cefa0..e7493850aa7c 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -17,7 +17,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![crate_name = "rustc_mir"] #![crate_type = "rlib"] #![crate_type = "dylib"] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![unstable(feature = "rustc_private", issue = "27812")] #![feature(associated_consts)] diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index cbc53ea3c51c..453c3e43b6b8 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -18,19 +18,18 @@ use build; use rustc::dep_graph::DepNode; -use rustc::hir::def_id::DefId; use rustc::mir::Mir; use rustc::mir::transform::MirSource; use rustc::mir::visit::MutVisitor; use pretty; use hair::cx::Cx; -use rustc::infer::InferCtxtBuilder; +use rustc::infer::InferCtxt; use rustc::traits::Reveal; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::hir; -use rustc::hir::intravisit::{self, FnKind, Visitor, NestedVisitorMap}; +use rustc::hir::intravisit::{Visitor, NestedVisitorMap}; use syntax::abi::Abi; use syntax::ast; use syntax_pos::Span; @@ -80,161 +79,27 @@ struct BuildMir<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } -/// Helper type of a temporary returned by BuildMir::cx(...). -/// Necessary because we can't write the following bound: -/// F: for<'b, 'tcx> where 'gcx: 'tcx FnOnce(Cx<'b, 'gcx, 'tcx>). -struct CxBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { - src: MirSource, - def_id: DefId, - infcx: InferCtxtBuilder<'a, 'gcx, 'tcx> -} +fn build<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, + body_id: hir::BodyId) + -> (Mir<'tcx>, MirSource) { + let tcx = infcx.tcx.global_tcx(); -impl<'a, 'gcx, 'tcx> BuildMir<'a, 'gcx> { - fn cx<'b>(&'b mut self, src: MirSource) -> CxBuilder<'b, 'gcx, 'tcx> { - let param_env = ty::ParameterEnvironment::for_item(self.tcx, src.item_id()); - let def_id = self.tcx.map.local_def_id(src.item_id()); - CxBuilder { - src: src, - infcx: self.tcx.infer_ctxt(None, Some(param_env), Reveal::NotSpecializable), - def_id: def_id - } - } -} - -impl<'a, 'gcx, 'tcx> CxBuilder<'a, 'gcx, 'tcx> { - fn build(&'tcx mut self, f: F) - where F: for<'b> FnOnce(Cx<'b, 'gcx, 'tcx>) -> Mir<'tcx> - { - let (src, def_id) = (self.src, self.def_id); - self.infcx.enter(|infcx| { - let mut mir = f(Cx::new(&infcx, src)); - - // Convert the Mir to global types. - let tcx = infcx.tcx.global_tcx(); - let mut globalizer = GlobalizeMir { - tcx: tcx, - span: mir.span - }; - globalizer.visit_mir(&mut mir); - let mir = unsafe { - mem::transmute::>(mir) - }; - - pretty::dump_mir(tcx, "mir_map", &0, src, &mir); - - let mir = tcx.alloc_mir(mir); - assert!(tcx.mir_map.borrow_mut().insert(def_id, mir).is_none()); - }); - } -} - -impl<'a, 'gcx> BuildMir<'a, 'gcx> { - fn build_const_integer(&mut self, body: hir::BodyId) { - let body = self.tcx.map.body(body); - // FIXME(eddyb) Closures should have separate - // function definition IDs and expression IDs. - // Type-checking should not let closures get - // this far in an integer constant position. - if let hir::ExprClosure(..) = body.value.node { - return; - } - self.cx(MirSource::Const(body.value.id)).build(|cx| { - build::construct_const(cx, body.value.id, body.id()) - }); - } -} - -impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.tcx.map) - } - - // Const and static items. - fn visit_item(&mut self, item: &'tcx hir::Item) { - match item.node { - hir::ItemConst(_, expr) => { - self.cx(MirSource::Const(item.id)).build(|cx| { - build::construct_const(cx, item.id, expr) - }); - } - hir::ItemStatic(_, m, expr) => { - self.cx(MirSource::Static(item.id, m)).build(|cx| { - build::construct_const(cx, item.id, expr) - }); - } - _ => {} - } - intravisit::walk_item(self, item); - } - - // Trait associated const defaults. - fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem) { - if let hir::TraitItemKind::Const(_, Some(expr)) = item.node { - self.cx(MirSource::Const(item.id)).build(|cx| { - build::construct_const(cx, item.id, expr) - }); - } - intravisit::walk_trait_item(self, item); - } - - // Impl associated const. - fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem) { - if let hir::ImplItemKind::Const(_, expr) = item.node { - self.cx(MirSource::Const(item.id)).build(|cx| { - build::construct_const(cx, item.id, expr) - }); - } - intravisit::walk_impl_item(self, item); - } - - // Repeat counts, i.e. [expr; constant]. - fn visit_expr(&mut self, expr: &'tcx hir::Expr) { - if let hir::ExprRepeat(_, count) = expr.node { - self.build_const_integer(count); - } - intravisit::walk_expr(self, expr); - } - - // Array lengths, i.e. [T; constant]. - fn visit_ty(&mut self, ty: &'tcx hir::Ty) { - if let hir::TyArray(_, length) = ty.node { - self.build_const_integer(length); - } - intravisit::walk_ty(self, ty); - } - - // Enum variant discriminant values. - fn visit_variant(&mut self, v: &'tcx hir::Variant, - g: &'tcx hir::Generics, item_id: ast::NodeId) { - if let Some(expr) = v.node.disr_expr { - self.build_const_integer(expr); - } - intravisit::walk_variant(self, v, g, item_id); - } - - fn visit_fn(&mut self, - fk: FnKind<'tcx>, - decl: &'tcx hir::FnDecl, - body_id: hir::BodyId, - span: Span, - id: ast::NodeId) { + let item_id = tcx.map.body_owner(body_id); + let src = MirSource::from_node(tcx, item_id); + let cx = Cx::new(infcx, src); + if let MirSource::Fn(id) = src { // fetch the fully liberated fn signature (that is, all bound // types/lifetimes replaced) - let fn_sig = match self.tcx.tables().liberated_fn_sigs.get(&id) { - Some(f) => f.clone(), - None => { - span_bug!(span, "no liberated fn sig for {:?}", id); - } - }; + let fn_sig = cx.tables().liberated_fn_sigs[&id].clone(); - let (abi, implicit_argument) = if let FnKind::Closure(..) = fk { - (Abi::Rust, Some((closure_self_ty(self.tcx, id, body_id.node_id), None))) + let ty = tcx.item_type(tcx.map.local_def_id(id)); + let (abi, implicit_argument) = if let ty::TyClosure(..) = ty.sty { + (Abi::Rust, Some((closure_self_ty(tcx, id, body_id), None))) } else { - let def_id = self.tcx.map.local_def_id(id); - (self.tcx.item_type(def_id).fn_abi(), None) + (ty.fn_abi(), None) }; - let body = self.tcx.map.body(body_id); + let body = tcx.map.body(body_id); let explicit_arguments = body.arguments .iter() @@ -244,25 +109,55 @@ impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> { }); let arguments = implicit_argument.into_iter().chain(explicit_arguments); - self.cx(MirSource::Fn(id)).build(|cx| { - build::construct_fn(cx, id, arguments, abi, fn_sig.output(), body) + (build::construct_fn(cx, id, arguments, abi, fn_sig.output(), body), src) + } else { + (build::construct_const(cx, body_id), src) + } +} + +impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + NestedVisitorMap::None + } + + fn visit_nested_body(&mut self, body_id: hir::BodyId) { + self.tcx.infer_ctxt(body_id, Reveal::NotSpecializable).enter(|infcx| { + let (mut mir, src) = build(&infcx, body_id); + + // Convert the Mir to global types. + let tcx = infcx.tcx.global_tcx(); + let mut globalizer = GlobalizeMir { + tcx: tcx, + span: mir.span + }; + globalizer.visit_mir(&mut mir); + let mir = unsafe { + mem::transmute::>(mir) + }; + + pretty::dump_mir(tcx, "mir_map", &0, src, &mir); + + let mir = tcx.alloc_mir(mir); + let def_id = tcx.map.local_def_id(src.item_id()); + assert!(tcx.mir_map.borrow_mut().insert(def_id, mir).is_none()); }); - intravisit::walk_fn(self, fk, decl, body_id, span, id); + let body = self.tcx.map.body(body_id); + self.visit_body(body); } } fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, closure_expr_id: ast::NodeId, - body_id: ast::NodeId) + body_id: hir::BodyId) -> Ty<'tcx> { - let closure_ty = tcx.tables().node_id_to_type(closure_expr_id); + let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_id); // We're just hard-coding the idea that the signature will be // &self or &mut self and hence will have a bound region with // number 0, hokey. let region = ty::Region::ReFree(ty::FreeRegion { - scope: tcx.region_maps.item_extent(body_id), + scope: tcx.region_maps.item_extent(body_id.node_id), bound_region: ty::BoundRegion::BrAnon(0), }); let region = tcx.mk_region(region); diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 41698574e0f1..57cf4b1e8b02 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -66,6 +66,7 @@ impl TempState { /// A "root candidate" for promotion, which will become the /// returned value in a promoted MIR, unless it's a subset /// of a larger candidate. +#[derive(Debug)] pub enum Candidate { /// Borrow of a constant temporary. Ref(Location), @@ -96,11 +97,8 @@ impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> { // Ignore drops, if the temp gets promoted, // then it's constant and thus drop is noop. // Storage live ranges are also irrelevant. - match context { - LvalueContext::Drop | - LvalueContext::StorageLive | - LvalueContext::StorageDead => return, - _ => {} + if context.is_drop() || context.is_storage_marker() { + return; } let temp = &mut self.temps[index]; @@ -117,15 +115,17 @@ impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> { _ => { /* mark as unpromotable below */ } } } else if let TempState::Defined { ref mut uses, .. } = *temp { - match context { - LvalueContext::Borrow {..} | - LvalueContext::Consume | - LvalueContext::Inspect => { - *uses += 1; - return; - } - _ => { /* mark as unpromotable below */ } + // We always allow borrows, even mutable ones, as we need + // to promote mutable borrows of some ZSTs e.g. `&mut []`. + let allowed_use = match context { + LvalueContext::Borrow {..} => true, + _ => context.is_nonmutating_use() + }; + if allowed_use { + *uses += 1; + return; } + /* mark as unpromotable below */ } *temp = TempState::Unpromotable; } @@ -190,15 +190,12 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { /// promoted MIR, recursing through temps. fn promote_temp(&mut self, temp: Local) -> Local { let old_keep_original = self.keep_original; - let (bb, stmt_idx) = match self.temps[temp] { - TempState::Defined { - location: Location { block, statement_index }, - uses - } if uses > 0 => { + let loc = match self.temps[temp] { + TempState::Defined { location, uses } if uses > 0 => { if uses > 1 { self.keep_original = true; } - (block, statement_index) + location } state => { span_bug!(self.promoted.span, "{:?} not promotable: {:?}", @@ -209,91 +206,82 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { self.temps[temp] = TempState::PromotedOut; } - let no_stmts = self.source[bb].statements.len(); + let no_stmts = self.source[loc.block].statements.len(); + let new_temp = self.promoted.local_decls.push( + LocalDecl::new_temp(self.source.local_decls[temp].ty)); + + debug!("promote({:?} @ {:?}/{:?}, {:?})", + temp, loc, no_stmts, self.keep_original); // First, take the Rvalue or Call out of the source MIR, // or duplicate it, depending on keep_original. - let (mut rvalue, mut call) = (None, None); - let source_info = if stmt_idx < no_stmts { - let statement = &mut self.source[bb].statements[stmt_idx]; - let rhs = match statement.kind { - StatementKind::Assign(_, ref mut rhs) => rhs, - _ => { - span_bug!(statement.source_info.span, "{:?} is not an assignment", - statement); + if loc.statement_index < no_stmts { + let (mut rvalue, source_info) = { + let statement = &mut self.source[loc.block].statements[loc.statement_index]; + let rhs = match statement.kind { + StatementKind::Assign(_, ref mut rhs) => rhs, + _ => { + span_bug!(statement.source_info.span, "{:?} is not an assignment", + statement); + } + }; + + (if self.keep_original { + rhs.clone() + } else { + let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]); + mem::replace(rhs, unit) + }, statement.source_info) + }; + + self.visit_rvalue(&mut rvalue, loc); + self.assign(new_temp, rvalue, source_info.span); + } else { + let terminator = if self.keep_original { + self.source[loc.block].terminator().clone() + } else { + let terminator = self.source[loc.block].terminator_mut(); + let target = match terminator.kind { + TerminatorKind::Call { destination: Some((_, target)), .. } => target, + ref kind => { + span_bug!(terminator.source_info.span, "{:?} not promotable", kind); + } + }; + Terminator { + source_info: terminator.source_info, + kind: mem::replace(&mut terminator.kind, TerminatorKind::Goto { + target: target + }) } }; - if self.keep_original { - rvalue = Some(rhs.clone()); - } else { - let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]); - rvalue = Some(mem::replace(rhs, unit)); - } - statement.source_info - } else if self.keep_original { - let terminator = self.source[bb].terminator().clone(); - call = Some(terminator.kind); - terminator.source_info - } else { - let terminator = self.source[bb].terminator_mut(); - let target = match terminator.kind { - TerminatorKind::Call { - destination: ref mut dest @ Some(_), - ref mut cleanup, .. - } => { - // No cleanup necessary. - cleanup.take(); - // We'll put a new destination in later. - dest.take().unwrap().1 + match terminator.kind { + TerminatorKind::Call { mut func, mut args, .. } => { + self.visit_operand(&mut func, loc); + for arg in &mut args { + self.visit_operand(arg, loc); + } + + let last = self.promoted.basic_blocks().last().unwrap(); + let new_target = self.new_block(); + + *self.promoted[last].terminator_mut() = Terminator { + kind: TerminatorKind::Call { + func: func, + args: args, + cleanup: None, + destination: Some((Lvalue::Local(new_temp), new_target)) + }, + ..terminator + }; } ref kind => { span_bug!(terminator.source_info.span, "{:?} not promotable", kind); } }; - call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto { - target: target - })); - terminator.source_info }; - // Then, recurse for components in the Rvalue or Call. - if stmt_idx < no_stmts { - self.visit_rvalue(rvalue.as_mut().unwrap(), Location { - block: bb, - statement_index: stmt_idx - }); - } else { - self.visit_terminator_kind(bb, call.as_mut().unwrap(), Location { - block: bb, - statement_index: no_stmts - }); - } - - let new_temp = self.promoted.local_decls.push( - LocalDecl::new_temp(self.source.local_decls[temp].ty)); - - // Inject the Rvalue or Call into the promoted MIR. - if stmt_idx < no_stmts { - self.assign(new_temp, rvalue.unwrap(), source_info.span); - } else { - let last = self.promoted.basic_blocks().last().unwrap(); - let new_target = self.new_block(); - let mut call = call.unwrap(); - match call { - TerminatorKind::Call { ref mut destination, ..} => { - *destination = Some((Lvalue::Local(new_temp), new_target)); - } - _ => bug!() - } - let terminator = self.promoted[last].terminator_mut(); - terminator.source_info.span = source_info.span; - terminator.kind = call; - } - - // Restore the old duplication state. self.keep_original = old_keep_original; - new_temp } @@ -355,6 +343,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, mut temps: IndexVec, candidates: Vec) { // Visit candidates in reverse, in case they're nested. + debug!("promote_candidates({:?})", candidates); for candidate in candidates.into_iter().rev() { let (span, ty) = match candidate { Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 893478a93318..fea27ee5c548 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -993,9 +993,9 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants { Entry::Vacant(entry) => { // Guard against `const` recursion. entry.insert(Qualif::RECURSIVE); + Mode::Const } } - Mode::Const } MirSource::Static(_, hir::MutImmutable) => Mode::Static, MirSource::Static(_, hir::MutMutable) => Mode::StaticMut, @@ -1038,7 +1038,7 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants { // Statics must be Sync. if mode == Mode::Static { let ty = mir.return_ty; - tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| { + tcx.infer_ctxt((), Reveal::NotSpecializable).enter(|infcx| { let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic); let mut fulfillment_cx = traits::FulfillmentContext::new(); fulfillment_cx.register_bound(&infcx, ty, diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 4c86331a5257..0fabbe6678ad 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -725,7 +725,7 @@ impl<'tcx> MirPass<'tcx> for TypeckMir { return; } let param_env = ty::ParameterEnvironment::for_item(tcx, src.item_id()); - tcx.infer_ctxt(None, Some(param_env), Reveal::NotSpecializable).enter(|infcx| { + tcx.infer_ctxt(param_env, Reveal::NotSpecializable).enter(|infcx| { let mut checker = TypeChecker::new(&infcx, src.item_id()); { let mut verifier = TypeVerifier::new(&mut checker, mir); diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index d1d9a201567b..8f12817511ad 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -26,10 +26,9 @@ use rustc::dep_graph::DepNode; use rustc::ty::cast::CastKind; -use rustc_const_eval::{ConstEvalErr, compare_lit_exprs}; -use rustc_const_eval::{eval_const_expr_partial}; +use rustc_const_eval::{ConstEvalErr, ConstContext}; use rustc_const_eval::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll, Math}; -use rustc_const_eval::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath}; +use rustc_const_eval::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath, BadType}; use rustc_const_eval::ErrKind::UnresolvedPath; use rustc_const_eval::EvalHint::ExprTypeChecked; use rustc_const_math::{ConstMathErr, Op}; @@ -61,15 +60,18 @@ struct CheckCrateVisitor<'a, 'tcx: 'a> { promotable: bool, mut_rvalue_borrows: NodeSet, param_env: ty::ParameterEnvironment<'tcx>, + tables: &'a ty::Tables<'tcx>, } impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { fn check_const_eval(&self, expr: &'gcx hir::Expr) { - if let Err(err) = eval_const_expr_partial(self.tcx, expr, ExprTypeChecked, None) { + let const_cx = ConstContext::with_tables(self.tcx, self.tables); + if let Err(err) = const_cx.eval(expr, ExprTypeChecked) { match err.kind { UnimplementedConstVal(_) => {} IndexOpFeatureGated => {} ErroneousReferencedConstant(_) => {} + BadType(_) => {} _ => { self.tcx.sess.add_lint(CONST_ERR, expr.id, @@ -111,8 +113,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { NestedVisitorMap::None } - fn visit_nested_body(&mut self, body: hir::BodyId) { - match self.tcx.rvalue_promotable_to_static.borrow_mut().entry(body.node_id) { + fn visit_nested_body(&mut self, body_id: hir::BodyId) { + match self.tcx.rvalue_promotable_to_static.borrow_mut().entry(body_id.node_id) { Entry::Occupied(_) => return, Entry::Vacant(entry) => { // Prevent infinite recursion on re-entry. @@ -120,7 +122,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { } } - let item_id = self.tcx.map.body_owner(body); + let item_id = self.tcx.map.body_owner(body_id); let outer_in_fn = self.in_fn; self.in_fn = match MirSource::from_node(self.tcx, item_id) { @@ -128,19 +130,25 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { _ => false }; - let body = self.tcx.map.body(body); + let outer_tables = self.tables; + self.tables = self.tcx.item_tables(self.tcx.map.local_def_id(item_id)); + + let body = self.tcx.map.body(body_id); if !self.in_fn { self.check_const_eval(&body.value); } - let param_env = ty::ParameterEnvironment::for_item(self.tcx, item_id); - let outer_param_env = mem::replace(&mut self.param_env, param_env); - self.tcx.infer_ctxt(None, Some(self.param_env.clone()), Reveal::NotSpecializable) - .enter(|infcx| euv::ExprUseVisitor::new(self, &infcx).consume_body(body)); + let outer_penv = self.tcx.infer_ctxt(body_id, Reveal::NotSpecializable).enter(|infcx| { + let param_env = infcx.parameter_environment.clone(); + let outer_penv = mem::replace(&mut self.param_env, param_env); + euv::ExprUseVisitor::new(self, &infcx).consume_body(body); + outer_penv + }); self.visit_body(body); - self.param_env = outer_param_env; + self.param_env = outer_penv; + self.tables = outer_tables; self.in_fn = outer_in_fn; } @@ -150,10 +158,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { self.check_const_eval(lit); } PatKind::Range(ref start, ref end) => { - self.check_const_eval(start); - self.check_const_eval(end); - - match compare_lit_exprs(self.tcx, p.span, start, end) { + let const_cx = ConstContext::with_tables(self.tcx, self.tables); + match const_cx.compare_lit_exprs(p.span, start, end) { Ok(Ordering::Less) | Ok(Ordering::Equal) => {} Ok(Ordering::Greater) => { @@ -193,7 +199,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { let outer = self.promotable; self.promotable = true; - let node_ty = self.tcx.tables().node_id_to_type(ex.id); + let node_ty = self.tables.node_id_to_type(ex.id); check_expr(self, ex, node_ty); check_adjustments(self, ex); @@ -219,7 +225,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { } if self.in_fn && self.promotable { - match eval_const_expr_partial(self.tcx, ex, ExprTypeChecked, None) { + let const_cx = ConstContext::with_tables(self.tcx, self.tables); + match const_cx.eval(ex, ExprTypeChecked) { Ok(_) => {} Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) | Err(ConstEvalErr { kind: MiscCatchAll, .. }) | @@ -230,6 +237,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), .. }) | Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), .. }) | Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {} + Err(ConstEvalErr { kind: BadType(_), .. }) => {} Err(msg) => { self.tcx.sess.add_lint(CONST_ERR, ex.id, @@ -262,14 +270,14 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node match e.node { hir::ExprUnary(..) | hir::ExprBinary(..) | - hir::ExprIndex(..) if v.tcx.tables().method_map.contains_key(&method_call) => { + hir::ExprIndex(..) if v.tables.method_map.contains_key(&method_call) => { v.promotable = false; } hir::ExprBox(_) => { v.promotable = false; } hir::ExprUnary(op, ref inner) => { - match v.tcx.tables().node_id_to_type(inner.id).sty { + match v.tables.node_id_to_type(inner.id).sty { ty::TyRawPtr(_) => { assert!(op == hir::UnDeref); @@ -279,7 +287,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } } hir::ExprBinary(op, ref lhs, _) => { - match v.tcx.tables().node_id_to_type(lhs.id).sty { + match v.tables.node_id_to_type(lhs.id).sty { ty::TyRawPtr(_) => { assert!(op.node == hir::BiEq || op.node == hir::BiNe || op.node == hir::BiLe || op.node == hir::BiLt || @@ -301,7 +309,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } } hir::ExprPath(ref qpath) => { - let def = v.tcx.tables().qpath_def(qpath, e.id); + let def = v.tables.qpath_def(qpath, e.id); match def { Def::VariantCtor(..) | Def::StructCtor(..) | Def::Fn(..) | Def::Method(..) => {} @@ -337,7 +345,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } // The callee is an arbitrary expression, it doesn't necessarily have a definition. let def = if let hir::ExprPath(ref qpath) = callee.node { - v.tcx.tables().qpath_def(qpath, callee.id) + v.tables.qpath_def(qpath, callee.id) } else { Def::Err }; @@ -359,14 +367,14 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node } } hir::ExprMethodCall(..) => { - let method = v.tcx.tables().method_map[&method_call]; + let method = v.tables.method_map[&method_call]; match v.tcx.associated_item(method.def_id).container { ty::ImplContainer(_) => v.handle_const_fn_call(method.def_id, node_ty), ty::TraitContainer(_) => v.promotable = false } } hir::ExprStruct(..) => { - if let ty::TyAdt(adt, ..) = v.tcx.tables().expr_ty(e).sty { + if let ty::TyAdt(adt, ..) = v.tables.expr_ty(e).sty { // unsafe_cell_type doesn't necessarily exist with no_core if Some(adt.did) == v.tcx.lang_items.unsafe_cell_type() { v.promotable = false; @@ -420,7 +428,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) { use rustc::ty::adjustment::*; - match v.tcx.tables().adjustments.get(&e.id).map(|adj| adj.kind) { + match v.tables.adjustments.get(&e.id).map(|adj| adj.kind) { None | Some(Adjust::NeverToAny) | Some(Adjust::ReifyFnPointer) | @@ -429,7 +437,7 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp Some(Adjust::DerefRef { autoderefs, .. }) => { if (0..autoderefs as u32) - .any(|autoderef| v.tcx.tables().is_overloaded_autoderef(e.id, autoderef)) { + .any(|autoderef| v.tables.is_overloaded_autoderef(e.id, autoderef)) { v.promotable = false; } } @@ -440,6 +448,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.visit_all_item_likes_in_krate(DepNode::CheckConst, &mut CheckCrateVisitor { tcx: tcx, + tables: &ty::Tables::empty(), in_fn: false, promotable: false, mut_rvalue_borrows: NodeSet(), diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 670ef426c2dd..143c1efed5a9 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -21,7 +21,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(rustc_diagnostic_macros)] #![feature(staged_api)] diff --git a/src/librustc_passes/rvalues.rs b/src/librustc_passes/rvalues.rs index 3da4f24b6c2f..78b591a48cca 100644 --- a/src/librustc_passes/rvalues.rs +++ b/src/librustc_passes/rvalues.rs @@ -14,11 +14,11 @@ use rustc::dep_graph::DepNode; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; -use rustc::ty::{self, TyCtxt, ParameterEnvironment}; +use rustc::ty::{self, TyCtxt}; use rustc::traits::Reveal; use rustc::hir; -use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; +use rustc::hir::intravisit::{Visitor, NestedVisitorMap}; use syntax::ast; use syntax_pos::Span; @@ -33,28 +33,19 @@ struct RvalueContext<'a, 'tcx: 'a> { impl<'a, 'tcx> Visitor<'tcx> for RvalueContext<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::OnlyBodies(&self.tcx.map) + NestedVisitorMap::None } - fn visit_fn(&mut self, - fk: intravisit::FnKind<'tcx>, - fd: &'tcx hir::FnDecl, - b: hir::BodyId, - s: Span, - fn_id: ast::NodeId) { - // FIXME (@jroesch) change this to be an inference context - let param_env = ParameterEnvironment::for_item(self.tcx, fn_id); - self.tcx.infer_ctxt(None, Some(param_env.clone()), - Reveal::NotSpecializable).enter(|infcx| { + fn visit_nested_body(&mut self, body_id: hir::BodyId) { + let body = self.tcx.map.body(body_id); + self.tcx.infer_ctxt(body_id, Reveal::NotSpecializable).enter(|infcx| { let mut delegate = RvalueContextDelegate { tcx: infcx.tcx, - param_env: ¶m_env + param_env: &infcx.parameter_environment }; - let body = infcx.tcx.map.body(b); - let mut euv = euv::ExprUseVisitor::new(&mut delegate, &infcx); - euv.consume_body(body); + euv::ExprUseVisitor::new(&mut delegate, &infcx).consume_body(body); }); - intravisit::walk_fn(self, fk, fd, b, s, fn_id) + self.visit_body(body); } } diff --git a/src/librustc_platform_intrinsics/lib.rs b/src/librustc_platform_intrinsics/lib.rs index e814050e9609..296c133115f4 100644 --- a/src/librustc_platform_intrinsics/lib.rs +++ b/src/librustc_platform_intrinsics/lib.rs @@ -13,7 +13,7 @@ #![crate_type = "dylib"] #![crate_type = "rlib"] #![feature(staged_api)] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![allow(bad_style)] pub struct Intrinsic { diff --git a/src/librustc_plugin/lib.rs b/src/librustc_plugin/lib.rs index 91e0fd636c9c..8d4e61ad8e54 100644 --- a/src/librustc_plugin/lib.rs +++ b/src/librustc_plugin/lib.rs @@ -57,7 +57,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(staged_api)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 81863a3fadfd..ef7a1f695ffb 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -15,7 +15,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] @@ -392,6 +392,7 @@ struct PrivacyVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, curitem: DefId, in_foreign: bool, + tables: &'a ty::Tables<'tcx>, } impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { @@ -435,6 +436,14 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivacyVisitor<'a, 'tcx> { NestedVisitorMap::All(&self.tcx.map) } + fn visit_nested_body(&mut self, body: hir::BodyId) { + let old_tables = self.tables; + self.tables = self.tcx.body_tables(body); + let body = self.tcx.map.body(body); + self.visit_body(body); + self.tables = old_tables; + } + fn visit_item(&mut self, item: &'tcx hir::Item) { let orig_curitem = replace(&mut self.curitem, self.tcx.map.local_def_id(item.id)); intravisit::walk_item(self, item); @@ -445,12 +454,12 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivacyVisitor<'a, 'tcx> { match expr.node { hir::ExprMethodCall(..) => { let method_call = ty::MethodCall::expr(expr.id); - let method = self.tcx.tables().method_map[&method_call]; + let method = self.tables.method_map[&method_call]; self.check_method(expr.span, method.def_id); } hir::ExprStruct(ref qpath, ref expr_fields, _) => { - let def = self.tcx.tables().qpath_def(qpath, expr.id); - let adt = self.tcx.tables().expr_ty(expr).ty_adt_def().unwrap(); + let def = self.tables.qpath_def(qpath, expr.id); + let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap(); let variant = adt.variant_of_def(def); // RFC 736: ensure all unmentioned fields are visible. // Rather than computing the set of unmentioned fields @@ -511,15 +520,15 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivacyVisitor<'a, 'tcx> { match pattern.node { PatKind::Struct(ref qpath, ref fields, _) => { - let def = self.tcx.tables().qpath_def(qpath, pattern.id); - let adt = self.tcx.tables().pat_ty(pattern).ty_adt_def().unwrap(); + let def = self.tables.qpath_def(qpath, pattern.id); + let adt = self.tables.pat_ty(pattern).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)); } } PatKind::TupleStruct(_, ref fields, ddpos) => { - match self.tcx.tables().pat_ty(pattern).sty { + match self.tables.pat_ty(pattern).sty { // enum fields have no privacy at this time ty::TyAdt(def, _) if !def.is_enum() => { let expected_len = def.struct_variant().fields.len(); @@ -1203,6 +1212,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, curitem: DefId::local(CRATE_DEF_INDEX), in_foreign: false, tcx: tcx, + tables: &ty::Tables::empty(), }; intravisit::walk_crate(&mut visitor, krate); diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 1b3791355d2c..5be21bc62c56 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -168,6 +168,7 @@ impl<'a> Resolver<'a> { target: binding, source: source, result: self.per_ns(|_, _| Cell::new(Err(Undetermined))), + type_ns_only: false, }; self.add_import_directive( module_path, subclass, view_path.span, item.id, vis, expansion, @@ -195,10 +196,10 @@ impl<'a> Resolver<'a> { for source_item in source_items { let node = source_item.node; - let (module_path, ident, rename) = { + let (module_path, ident, rename, type_ns_only) = { if node.name.name != keywords::SelfValue.name() { let rename = node.rename.unwrap_or(node.name); - (module_path.clone(), node.name, rename) + (module_path.clone(), node.name, rename, false) } else { let ident = *module_path.last().unwrap(); if ident.name == keywords::CrateRoot.name() { @@ -212,13 +213,14 @@ impl<'a> Resolver<'a> { } let module_path = module_path.split_last().unwrap().1; let rename = node.rename.unwrap_or(ident); - (module_path.to_vec(), ident, rename) + (module_path.to_vec(), ident, rename, true) } }; let subclass = SingleImport { target: rename, source: ident, result: self.per_ns(|_, _| Cell::new(Err(Undetermined))), + type_ns_only: type_ns_only, }; let id = source_item.node.id; self.add_import_directive( diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index b7908f0c0edd..56e8c75b859a 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -15,7 +15,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(associated_consts)] #![feature(rustc_diagnostic_macros)] @@ -894,6 +894,7 @@ enum NameBindingKind<'a> { binding: &'a NameBinding<'a>, directive: &'a ImportDirective<'a>, used: Cell, + legacy_self_import: bool, }, Ambiguity { b1: &'a NameBinding<'a>, @@ -1346,8 +1347,13 @@ impl<'a> Resolver<'a> { } match binding.kind { - NameBindingKind::Import { directive, binding, ref used } if !used.get() => { + NameBindingKind::Import { directive, binding, ref used, legacy_self_import } + if !used.get() => { used.set(true); + if legacy_self_import { + self.warn_legacy_self_import(directive); + return false; + } self.used_imports.insert((directive.id, ns)); self.add_to_glob_map(directive.id, ident); self.record_use(ident, ns, binding, span) @@ -2084,10 +2090,25 @@ impl<'a> Resolver<'a> { let expected = source.descr_expected(); let path_str = names_to_string(path); let code = source.error_code(def.is_some()); - let base_msg = if let Some(def) = def { - format!("expected {}, found {} `{}`", expected, def.kind_name(), path_str) + let (base_msg, fallback_label) = if let Some(def) = def { + (format!("expected {}, found {} `{}`", expected, def.kind_name(), path_str), + format!("not a {}", expected)) } else { - format!("unresolved {} `{}`", expected, path_str) + let item_str = path[path.len() - 1]; + let (mod_prefix, mod_str) = if path.len() == 1 { + (format!(""), format!("this scope")) + } else if path.len() == 2 && path[0].name == keywords::CrateRoot.name() { + (format!(""), format!("the crate root")) + } else { + let mod_path = &path[..path.len() - 1]; + let mod_prefix = match this.resolve_path(mod_path, Some(TypeNS), None) { + PathResult::Module(module) => module.def(), + _ => None, + }.map_or(format!(""), |def| format!("{} ", def.kind_name())); + (mod_prefix, format!("`{}`", names_to_string(mod_path))) + }; + (format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str), + format!("not found in {}", mod_str)) }; let mut err = this.session.struct_span_err_with_code(span, &base_msg, code); @@ -2170,19 +2191,13 @@ impl<'a> Resolver<'a> { } // Try Levenshtein if nothing else worked. - if path.len() == 1 { - if let Some(candidate) = this.lookup_typo_candidate(name, ns, is_expected) { - err.span_label(span, &format!("did you mean `{}`?", candidate)); - return err; - } + if let Some(candidate) = this.lookup_typo_candidate(path, ns, is_expected) { + err.span_label(span, &format!("did you mean `{}`?", candidate)); + return err; } - // Fallback labels. - if def.is_some() { - err.span_label(span, &format!("not a {}", expected)); - } else { - err.span_label(span, &format!("no resolution found")); - } + // Fallback label. + err.span_label(span, &fallback_label); err }; let report_errors = |this: &mut Self, def: Option| { @@ -2336,20 +2351,19 @@ impl<'a> Resolver<'a> { PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"), }; - if path.len() == 1 || global_by_default || result.base_def == Def::Err { - return Some(result); - } - - let unqualified_result = { - match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) { - PathResult::NonModule(path_res) => path_res.base_def, - PathResult::Module(module) => module.def().unwrap(), - _ => return Some(result), + if path.len() > 1 && !global_by_default && result.base_def != Def::Err && + path[0].name != keywords::CrateRoot.name() && path[0].name != "$crate" { + let unqualified_result = { + match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) { + PathResult::NonModule(path_res) => path_res.base_def, + PathResult::Module(module) => module.def().unwrap(), + _ => return Some(result), + } + }; + if result.base_def == unqualified_result { + let lint = lint::builtin::UNUSED_QUALIFICATIONS; + self.session.add_lint(lint, id, span, "unnecessary qualification".to_string()); } - }; - if result.base_def == unqualified_result && path[0].name != "$crate" { - let lint = lint::builtin::UNUSED_QUALIFICATIONS; - self.session.add_lint(lint, id, span, "unnecessary qualification".to_string()); } Some(result) @@ -2411,13 +2425,15 @@ impl<'a> Resolver<'a> { match binding { Ok(binding) => { + let def = binding.def(); + let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(def); if let Some(next_module) = binding.module() { module = Some(next_module); - } else if binding.def() == Def::Err { + } else if def == Def::Err { return PathResult::NonModule(err_path_resolution()); - } else if opt_ns.is_some() && !(opt_ns == Some(MacroNS) && !is_last) { + } else if opt_ns.is_some() && (is_last || maybe_assoc) { return PathResult::NonModule(PathResolution { - base_def: binding.def(), + base_def: def, depth: path.len() - i - 1, }); } else { @@ -2631,21 +2647,72 @@ impl<'a> Resolver<'a> { } fn lookup_typo_candidate(&mut self, - name: Name, + path: &[Ident], ns: Namespace, filter_fn: FilterFn) - -> Option + -> Option where FilterFn: Fn(Def) -> bool { - // FIXME: bindings in ribs provide quite modest set of candidates, - // extend it with other names in scope. - let names = self.ribs[ns].iter().rev().flat_map(|rib| { - rib.bindings.iter().filter_map(|(ident, def)| { - if filter_fn(*def) { Some(&ident.name) } else { None } - }) - }); - match find_best_match_for_name(names, &name.as_str(), None) { - Some(found) if found != name => Some(found), + let add_module_candidates = |module: Module, names: &mut Vec| { + for (&(ident, _), resolution) in module.resolutions.borrow().iter() { + if let Some(binding) = resolution.borrow().binding { + if filter_fn(binding.def()) { + names.push(ident.name); + } + } + } + }; + + let mut names = Vec::new(); + let prefix_str = if path.len() == 1 { + // Search in lexical scope. + // Walk backwards up the ribs in scope and collect candidates. + for rib in self.ribs[ns].iter().rev() { + // Locals and type parameters + for (ident, def) in &rib.bindings { + if filter_fn(*def) { + names.push(ident.name); + } + } + // Items in scope + if let ModuleRibKind(module) = rib.kind { + // Items from this module + add_module_candidates(module, &mut names); + + if let ModuleKind::Block(..) = module.kind { + // We can see through blocks + } else { + // Items from the prelude + if let Some(prelude) = self.prelude { + if !module.no_implicit_prelude { + add_module_candidates(prelude, &mut names); + } + } + break; + } + } + } + // Add primitive types to the mix + if filter_fn(Def::PrimTy(TyBool)) { + for (name, _) in &self.primitive_type_table.primitive_types { + names.push(*name); + } + } + String::new() + } else { + // Search in module. + let mod_path = &path[..path.len() - 1]; + if let PathResult::Module(module) = self.resolve_path(mod_path, Some(TypeNS), None) { + add_module_candidates(module, &mut names); + } + names_to_string(mod_path) + "::" + }; + + let name = path[path.len() - 1].name; + // Make sure error reporting is deterministic. + names.sort_by_key(|name| name.as_str()); + match find_best_match_for_name(names.iter(), &name.as_str(), None) { + Some(found) if found != name => Some(format!("{}{}", prefix_str, found)), _ => None, } } @@ -2982,8 +3049,8 @@ impl<'a> Resolver<'a> { let participle = |binding: &NameBinding| { if binding.is_import() { "imported" } else { "defined" } }; - let msg1 = format!("`{}` could resolve to the name {} here", name, participle(b1)); - let msg2 = format!("`{}` could also resolve to the name {} here", name, participle(b2)); + let msg1 = format!("`{}` could refer to the name {} here", name, participle(b1)); + let msg2 = format!("`{}` could also refer to the name {} here", name, participle(b2)); let note = if !lexical && b1.is_glob_import() { format!("consider adding an explicit import of `{}` to disambiguate", name) } else if let Def::Macro(..) = b1.def() { @@ -3111,6 +3178,12 @@ impl<'a> Resolver<'a> { err.emit(); self.name_already_seen.insert(name, span); } + + fn warn_legacy_self_import(&self, directive: &'a ImportDirective<'a>) { + let (id, span) = (directive.id, directive.span); + let msg = "`self` no longer imports values".to_string(); + self.session.add_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg); + } } fn is_struct_like(def: Def) -> bool { diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 47033f74e0e3..403797b6d318 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -380,8 +380,8 @@ impl<'a> Resolver<'a> { MacroBinding::Modern(binding) => (binding.span, "imported"), MacroBinding::Legacy(binding) => (binding.span, "defined"), }; - let msg1 = format!("`{}` could resolve to the macro {} here", ident, participle); - let msg2 = format!("`{}` could also resolve to the macro imported here", ident); + let msg1 = format!("`{}` could refer to the macro {} here", ident, participle); + let msg2 = format!("`{}` could also refer to the macro imported here", ident); self.session.struct_span_err(span, &format!("`{}` is ambiguous", ident)) .span_note(legacy_span, &msg1) .span_note(resolution.span, &msg2) diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 41d8f16b88df..65e599ac6c7e 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -21,6 +21,7 @@ use rustc::ty; use rustc::lint::builtin::PRIVATE_IN_PUBLIC; use rustc::hir::def_id::DefId; use rustc::hir::def::*; +use rustc::util::nodemap::FxHashSet; use syntax::ast::{Ident, NodeId}; use syntax::ext::base::Determinacy::{self, Determined, Undetermined}; @@ -40,6 +41,7 @@ pub enum ImportDirectiveSubclass<'a> { target: Ident, source: Ident, result: PerNS, Determinacy>>>, + type_ns_only: bool, }, GlobImport { is_prelude: bool, @@ -295,6 +297,7 @@ impl<'a> Resolver<'a> { binding: binding, directive: directive, used: Cell::new(false), + legacy_self_import: false, }, span: directive.span, vis: vis, @@ -502,8 +505,9 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { }; directive.imported_module.set(Some(module)); - let (source, target, result) = match directive.subclass { - SingleImport { source, target, ref result } => (source, target, result), + let (source, target, result, type_ns_only) = match directive.subclass { + SingleImport { source, target, ref result, type_ns_only } => + (source, target, result, type_ns_only), GlobImport { .. } => { self.resolve_glob_import(directive); return true; @@ -512,7 +516,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { }; let mut indeterminate = false; - self.per_ns(|this, ns| { + self.per_ns(|this, ns| if !type_ns_only || ns == TypeNS { if let Err(Undetermined) = result[ns].get() { result[ns].set(this.resolve_ident_in_module(module, source, ns, false, None)); } else { @@ -572,8 +576,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { _ => return None, }; - let (ident, result) = match directive.subclass { - SingleImport { source, ref result, .. } => (source, result), + let (ident, result, type_ns_only) = match directive.subclass { + SingleImport { source, ref result, type_ns_only, .. } => (source, result, type_ns_only), GlobImport { .. } if module.def_id() == directive.parent.def_id() => { // Importing a module into itself is not allowed. return Some("Cannot glob-import a module into itself.".to_string()); @@ -591,7 +595,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { }; let mut all_ns_err = true; - self.per_ns(|this, ns| { + let mut legacy_self_import = None; + self.per_ns(|this, ns| if !type_ns_only || ns == TypeNS { if let Ok(binding) = result[ns].get() { all_ns_err = false; if this.record_use(ident, ns, binding, directive.span) { @@ -599,11 +604,27 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { Some(this.dummy_binding); } } + } else if let Ok(binding) = this.resolve_ident_in_module(module, ident, ns, false, None) { + legacy_self_import = Some(directive); + let binding = this.arenas.alloc_name_binding(NameBinding { + kind: NameBindingKind::Import { + binding: binding, + directive: directive, + used: Cell::new(false), + legacy_self_import: true, + }, + ..*binding + }); + let _ = this.try_define(directive.parent, ident, ns, binding); }); if all_ns_err { + if let Some(directive) = legacy_self_import { + self.warn_legacy_self_import(directive); + return None; + } let mut all_ns_failed = true; - self.per_ns(|this, ns| { + self.per_ns(|this, ns| if !type_ns_only || ns == TypeNS { match this.resolve_ident_in_module(module, ident, ns, false, Some(span)) { Ok(_) => all_ns_failed = false, _ => {} @@ -728,7 +749,12 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { let mut reexports = Vec::new(); if module as *const _ == self.graph_root as *const _ { - reexports = mem::replace(&mut self.macro_exports, Vec::new()); + let mut exported_macro_names = FxHashSet(); + for export in mem::replace(&mut self.macro_exports, Vec::new()).into_iter().rev() { + if exported_macro_names.insert(export.name) { + reexports.push(export); + } + } } for (&(ident, ns), resolution) in module.resolutions.borrow().iter() { diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 093a739c69ff..4e4b35702898 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -99,7 +99,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { } } - fn nest(&mut self, scope_id: NodeId, f: F) + fn nest_scope(&mut self, scope_id: NodeId, f: F) where F: FnOnce(&mut DumpVisitor<'l, 'tcx, 'll, D>) { let parent_scope = self.cur_scope; @@ -108,6 +108,16 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { self.cur_scope = parent_scope; } + fn nest_tables(&mut self, item_id: NodeId, f: F) + where F: FnOnce(&mut DumpVisitor<'l, 'tcx, 'll, D>) + { + let old_tables = self.save_ctxt.tables; + let item_def_id = self.tcx.map.local_def_id(item_id); + self.save_ctxt.tables = self.tcx.item_tables(item_def_id); + f(self); + self.save_ctxt.tables = old_tables; + } + pub fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) { let source_file = self.tcx.sess.local_crate_source_file.as_ref(); let crate_root = source_file.map(|source_file| { @@ -337,7 +347,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { collector.visit_pat(&arg.pat); let span_utils = self.span.clone(); for &(id, ref p, ..) in &collector.collected_paths { - let typ = match self.tcx.tables().node_types.get(&id) { + let typ = match self.save_ctxt.tables.node_types.get(&id) { Some(s) => s.to_string(), None => continue, }; @@ -378,7 +388,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { let sig_str = ::make_signature(&sig.decl, &sig.generics); if body.is_some() { - self.process_formals(&sig.decl.inputs, &method_data.qualname); + self.nest_tables(id, |v| { + v.process_formals(&sig.decl.inputs, &method_data.qualname) + }); } // If the method is defined in an impl, then try and find the corresponding @@ -448,7 +460,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { // walk the fn body if let Some(body) = body { - self.nest(id, |v| v.visit_block(body)); + self.nest_tables(id, |v| v.nest_scope(id, |v| v.visit_block(body))); } } @@ -458,9 +470,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { if !self.span.filter_generated(Some(trait_ref_data.span), trait_ref.path.span) { self.dumper.type_ref(trait_ref_data.lower(self.tcx)); } - - visit::walk_path(self, &trait_ref.path); } + self.process_path(trait_ref.ref_id, &trait_ref.path, Some(recorder::TypeRef)); } fn process_struct_field_def(&mut self, field: &ast::StructField, parent_id: NodeId) { @@ -520,7 +531,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { self.dumper.function(fn_data.clone().lower(self.tcx)); } - self.process_formals(&decl.inputs, &fn_data.qualname); + self.nest_tables(item.id, |v| v.process_formals(&decl.inputs, &fn_data.qualname)); self.process_generic_params(ty_params, item.span, &fn_data.qualname, item.id); } @@ -532,7 +543,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { self.visit_ty(&ret_ty); } - self.nest(item.id, |v| v.visit_block(&body)); + self.nest_tables(item.id, |v| v.nest_scope(item.id, |v| v.visit_block(&body))); } fn process_static_or_const_item(&mut self, @@ -744,8 +755,6 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { if !self.span.filter_generated(Some(trait_ref_data.span), item.span) { self.dumper.type_ref(trait_ref_data.clone().lower(self.tcx)); } - - visit::walk_path(self, &trait_ref.as_ref().unwrap().path); } if !self.span.filter_generated(Some(impl_data.span), item.span) { @@ -761,6 +770,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { if !has_self_ref { self.visit_ty(&typ); } + if let &Some(ref trait_ref) = trait_ref { + self.process_path(trait_ref.ref_id, &trait_ref.path, Some(recorder::TypeRef)); + } self.process_generic_params(type_parameters, item.span, "", item.id); for impl_item in impl_items { let map = &self.tcx.map; @@ -989,9 +1001,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { fn process_pat(&mut self, p: &'l ast::Pat) { match p.node { - PatKind::Struct(ref path, ref fields, _) => { - visit::walk_path(self, path); - let adt = match self.tcx.tables().node_id_to_type_opt(p.id) { + PatKind::Struct(ref _path, ref fields, _) => { + // FIXME do something with _path? + let adt = match self.save_ctxt.tables.node_id_to_type_opt(p.id) { Some(ty) => ty.ty_adt_def().unwrap(), None => { visit::walk_pat(self, p); @@ -1032,7 +1044,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { ast::Mutability::Immutable => value.to_string(), _ => String::new(), }; - let typ = match self.tcx.tables().node_types.get(&id) { + let typ = match self.save_ctxt.tables.node_types.get(&id) { Some(typ) => { let typ = typ.to_string(); if !value.is_empty() { @@ -1286,7 +1298,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, self.process_trait(item, generics, trait_refs, methods), Mod(ref m) => { self.process_mod(item); - self.nest(item.id, |v| visit::walk_mod(v, m)); + self.nest_scope(item.id, |v| visit::walk_mod(v, m)); } Ty(ref ty, ref ty_params) => { let qualname = format!("::{}", self.tcx.node_path_str(item.id)); @@ -1331,23 +1343,26 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, self.process_macro_use(t.span, t.id); match t.node { ast::TyKind::Path(_, ref path) => { - if self.span.filter_generated(None, t.span) { + if generated_code(t.span) { return; } if let Some(id) = self.lookup_def_id(t.id) { - let sub_span = self.span.sub_span_for_type_name(t.span); - self.dumper.type_ref(TypeRefData { - span: sub_span.expect("No span found for type ref"), - ref_id: Some(id), - scope: self.cur_scope, - qualname: String::new() - }.lower(self.tcx)); + if let Some(sub_span) = self.span.sub_span_for_type_name(t.span) { + self.dumper.type_ref(TypeRefData { + span: sub_span, + ref_id: Some(id), + scope: self.cur_scope, + qualname: String::new() + }.lower(self.tcx)); + } } self.write_sub_paths_truncated(path); - - visit::walk_path(self, path); + } + ast::TyKind::Array(ref element, ref length) => { + self.visit_ty(element); + self.nest_tables(length.id, |v| v.visit_expr(length)); } _ => visit::walk_ty(self, t), } @@ -1367,7 +1382,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, } ast::ExprKind::Struct(ref path, ref fields, ref base) => { let hir_expr = self.save_ctxt.tcx.map.expect_expr(ex.id); - let adt = match self.tcx.tables().expr_ty_opt(&hir_expr) { + let adt = match self.save_ctxt.tables.expr_ty_opt(&hir_expr) { Some(ty) => ty.ty_adt_def().unwrap(), None => { visit::walk_expr(self, ex); @@ -1399,7 +1414,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, return; } }; - let ty = match self.tcx.tables().expr_ty_adjusted_opt(&hir_node) { + let ty = match self.save_ctxt.tables.expr_ty_adjusted_opt(&hir_node) { Some(ty) => &ty.sty, None => { visit::walk_expr(self, ex); @@ -1427,7 +1442,6 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, ast::ExprKind::Closure(_, ref decl, ref body, _fn_decl_span) => { let mut id = String::from("$"); id.push_str(&ex.id.to_string()); - self.process_formals(&decl.inputs, &id); // walk arg and return types for arg in &decl.inputs { @@ -1439,7 +1453,10 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, } // walk the body - self.nest(ex.id, |v| v.visit_expr(body)); + self.nest_tables(ex.id, |v| { + v.process_formals(&decl.inputs, &id); + v.nest_scope(ex.id, |v| v.visit_expr(body)) + }); } ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) | ast::ExprKind::WhileLet(ref pattern, ref subexpression, ref block, _) => { @@ -1455,6 +1472,10 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, visit::walk_block(self, block); opt_else.as_ref().map(|el| visit::walk_expr(self, el)); } + ast::ExprKind::Repeat(ref element, ref count) => { + self.visit_expr(element); + self.nest_tables(count.id, |v| v.visit_expr(count)); + } _ => { visit::walk_expr(self, ex) } @@ -1492,7 +1513,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, } else { "".to_string() }; - let typ = self.tcx.tables().node_types + let typ = self.save_ctxt.tables.node_types .get(&id).map(|t| t.to_string()).unwrap_or(String::new()); value.push_str(": "); value.push_str(&typ); diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 43e3b1dd2b50..0fbe29880b99 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -15,7 +15,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(custom_attribute)] #![allow(unused_attributes)] @@ -84,6 +84,7 @@ pub mod recorder { pub struct SaveContext<'l, 'tcx: 'l> { tcx: TyCtxt<'l, 'tcx, 'tcx>, + tables: &'l ty::Tables<'tcx>, analysis: &'l ty::CrateAnalysis<'tcx>, span_utils: SpanUtils<'tcx>, } @@ -93,24 +94,6 @@ macro_rules! option_try( ); impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { - pub fn new(tcx: TyCtxt<'l, 'tcx, 'tcx>, - analysis: &'l ty::CrateAnalysis<'tcx>) - -> SaveContext<'l, 'tcx> { - let span_utils = SpanUtils::new(&tcx.sess); - SaveContext::from_span_utils(tcx, analysis, span_utils) - } - - pub fn from_span_utils(tcx: TyCtxt<'l, 'tcx, 'tcx>, - analysis: &'l ty::CrateAnalysis<'tcx>, - span_utils: SpanUtils<'tcx>) - -> SaveContext<'l, 'tcx> { - SaveContext { - tcx: tcx, - analysis: analysis, - span_utils: span_utils, - } - } - // List external crates used by the current crate. pub fn get_external_crates(&self) -> Vec { let mut result = Vec::new(); @@ -460,7 +443,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { pub fn get_expr_data(&self, expr: &ast::Expr) -> Option { let hir_node = self.tcx.map.expect_expr(expr.id); - let ty = self.tcx.tables().expr_ty_adjusted_opt(&hir_node); + let ty = self.tables.expr_ty_adjusted_opt(&hir_node); if ty.is_none() || ty.unwrap().sty == ty::TyError { return None; } @@ -474,7 +457,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { return None; } }; - match self.tcx.tables().expr_ty_adjusted(&hir_node).sty { + match self.tables.expr_ty_adjusted(&hir_node).sty { ty::TyAdt(def, _) if !def.is_enum() => { let f = def.struct_variant().field_named(ident.node.name); let sub_span = self.span_utils.span_for_last_ident(expr.span); @@ -493,7 +476,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } } ast::ExprKind::Struct(ref path, ..) => { - match self.tcx.tables().expr_ty_adjusted(&hir_node).sty { + match self.tables.expr_ty_adjusted(&hir_node).sty { ty::TyAdt(def, _) if !def.is_enum() => { let sub_span = self.span_utils.span_for_last_ident(path.span); filter!(self.span_utils, sub_span, path.span, None); @@ -514,7 +497,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } ast::ExprKind::MethodCall(..) => { let method_call = ty::MethodCall::expr(expr.id); - let method_id = self.tcx.tables().method_map[&method_call].def_id; + let method_id = self.tables.method_map[&method_call].def_id; let (def_id, decl_id) = match self.tcx.associated_item(method_id).container { ty::ImplContainer(_) => (Some(method_id), None), ty::TraitContainer(_) => (None, Some(method_id)), @@ -551,7 +534,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { Node::NodePat(&hir::Pat { node: hir::PatKind::Path(ref qpath), .. }) | Node::NodePat(&hir::Pat { node: hir::PatKind::Struct(ref qpath, ..), .. }) | Node::NodePat(&hir::Pat { node: hir::PatKind::TupleStruct(ref qpath, ..), .. }) => { - self.tcx.tables().qpath_def(qpath, id) + self.tables.qpath_def(qpath, id) } Node::NodeLocal(&hir::Pat { node: hir::PatKind::Binding(_, def_id, ..), .. }) => { @@ -914,7 +897,12 @@ pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>, root_path.pop(); let output = &mut output_file; - let save_ctxt = SaveContext::new(tcx, analysis); + let save_ctxt = SaveContext { + tcx: tcx, + tables: &ty::Tables::empty(), + analysis: analysis, + span_utils: SpanUtils::new(&tcx.sess), + }; macro_rules! dump { ($new_dumper: expr) => {{ diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index b3eec5d66c46..ad4bb0fce22a 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -10,7 +10,8 @@ use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace}; use base; -use common::{type_is_fat_ptr, BlockAndBuilder, C_uint}; +use builder::Builder; +use common::{type_is_fat_ptr, C_uint}; use context::CrateContext; use cabi_x86; use cabi_x86_64; @@ -236,7 +237,7 @@ impl ArgType { /// lvalue for the original Rust type of this argument/return. /// Can be used for both storing formal arguments into Rust variables /// or results of call/invoke instructions into their destinations. - pub fn store(&self, bcx: &BlockAndBuilder, mut val: ValueRef, dst: ValueRef) { + pub fn store(&self, bcx: &Builder, mut val: ValueRef, dst: ValueRef) { if self.is_ignore() { return; } @@ -251,11 +252,8 @@ impl ArgType { let can_store_through_cast_ptr = false; if can_store_through_cast_ptr { let cast_dst = bcx.pointercast(dst, ty.ptr_to()); - let store = bcx.store(val, cast_dst); let llalign = llalign_of_min(ccx, self.ty); - unsafe { - llvm::LLVMSetAlignment(store, llalign); - } + bcx.store(val, cast_dst, Some(llalign)); } else { // The actual return type is a struct, but the ABI // adaptation code has cast it into some scalar type. The @@ -272,11 +270,11 @@ impl ArgType { // bitcasting to the struct type yields invalid cast errors. // We instead thus allocate some scratch space... - let llscratch = bcx.fcx().alloca(ty, "abi_cast"); + let llscratch = bcx.alloca(ty, "abi_cast"); base::Lifetime::Start.call(bcx, llscratch); // ...where we first store the value... - bcx.store(val, llscratch); + bcx.store(val, llscratch, None); // ...and then memcpy it to the intended destination. base::call_memcpy(bcx, @@ -292,18 +290,18 @@ impl ArgType { if self.original_ty == Type::i1(ccx) { val = bcx.zext(val, Type::i8(ccx)); } - bcx.store(val, dst); + bcx.store(val, dst, None); } } - pub fn store_fn_arg(&self, bcx: &BlockAndBuilder, idx: &mut usize, dst: ValueRef) { + pub fn store_fn_arg(&self, bcx: &Builder, idx: &mut usize, dst: ValueRef) { if self.pad.is_some() { *idx += 1; } if self.is_ignore() { return; } - let val = llvm::get_param(bcx.fcx().llfn, *idx as c_uint); + let val = llvm::get_param(bcx.llfn(), *idx as c_uint); *idx += 1; self.store(bcx, val, dst); } diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 31a5538a3c11..c3b9a56ac977 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -49,53 +49,20 @@ use llvm::{ValueRef, True, IntEQ, IntNE}; use rustc::ty::layout; use rustc::ty::{self, Ty, AdtKind}; use common::*; -use glue; +use builder::Builder; use base; use machine; use monomorphize; use type_::Type; use type_of; -use value::Value; - -#[derive(Copy, Clone, PartialEq)] -pub enum BranchKind { - Switch, - Single -} - -#[derive(Copy, Clone)] -pub struct MaybeSizedValue { - pub value: ValueRef, - pub meta: ValueRef, -} - -impl MaybeSizedValue { - pub fn sized(value: ValueRef) -> MaybeSizedValue { - MaybeSizedValue { - value: value, - meta: std::ptr::null_mut() - } - } - - pub fn unsized_(value: ValueRef, meta: ValueRef) -> MaybeSizedValue { - MaybeSizedValue { - value: value, - meta: meta - } - } - - pub fn has_meta(&self) -> bool { - !self.meta.is_null() - } -} /// Given an enum, struct, closure, or tuple, extracts fields. /// Treats closures as a struct with one variant. /// `empty_if_no_variants` is a switch to deal with empty enums. /// If true, `variant_index` is disregarded and an empty Vec returned in this case. -fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, - variant_index: usize, - empty_if_no_variants: bool) -> Vec> { +pub fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, + variant_index: usize, + empty_if_no_variants: bool) -> Vec> { match t.sty { ty::TyAdt(ref def, _) if def.variants.len() == 0 && empty_if_no_variants => { Vec::default() @@ -300,28 +267,6 @@ fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fields: &Vec> } } -/// Obtain a representation of the discriminant sufficient to translate -/// destructuring; this may or may not involve the actual discriminant. -pub fn trans_switch<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, - t: Ty<'tcx>, - scrutinee: ValueRef, - range_assert: bool -) -> (BranchKind, Option) { - let l = bcx.ccx.layout_of(t); - match *l { - layout::CEnum { .. } | layout::General { .. } | - layout::RawNullablePointer { .. } | layout::StructWrappedNullablePointer { .. } => { - (BranchKind::Switch, Some(trans_get_discr(bcx, t, scrutinee, None, range_assert))) - } - layout::Univariant { .. } | layout::UntaggedUnion { .. } => { - // N.B.: Univariant means <= 1 enum variants (*not* == 1 variants). - (BranchKind::Single, None) - }, - _ => bug!("{} is not an enum.", t) - } -} - pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool { match *l { layout::CEnum { signed, .. }=> signed, @@ -331,7 +276,7 @@ pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool { /// Obtain the actual discriminant of a value. pub fn trans_get_discr<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, scrutinee: ValueRef, cast_to: Option, @@ -358,7 +303,7 @@ pub fn trans_get_discr<'a, 'tcx>( layout::RawNullablePointer { nndiscr, .. } => { let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; let llptrty = type_of::sizing_type_of(bcx.ccx, - monomorphize::field_ty(bcx.ccx.tcx(), substs, + monomorphize::field_ty(bcx.tcx(), substs, &def.variants[nndiscr as usize].fields[0])); bcx.icmp(cmp, bcx.load(scrutinee), C_null(llptrty)) } @@ -374,7 +319,7 @@ pub fn trans_get_discr<'a, 'tcx>( } fn struct_wrapped_nullable_bitdiscr( - bcx: &BlockAndBuilder, + bcx: &Builder, nndiscr: u64, discrfield: &layout::FieldPath, scrutinee: ValueRef @@ -387,7 +332,7 @@ fn struct_wrapped_nullable_bitdiscr( } /// Helper for cases where the discriminant is simply loaded. -fn load_discr(bcx: &BlockAndBuilder, ity: layout::Integer, ptr: ValueRef, min: u64, max: u64, +fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef, min: u64, max: u64, range_assert: bool) -> ValueRef { let llty = Type::from_integer(bcx.ccx, ity); @@ -415,7 +360,7 @@ fn load_discr(bcx: &BlockAndBuilder, ity: layout::Integer, ptr: ValueRef, min: u /// discriminant-like value returned by `trans_switch`. /// /// This should ideally be less tightly tied to `_match`. -pub fn trans_case<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, t: Ty<'tcx>, value: Disr) -> ValueRef { +pub fn trans_case<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, value: Disr) -> ValueRef { let l = bcx.ccx.layout_of(t); match *l { layout::CEnum { discr, .. } @@ -435,19 +380,17 @@ pub fn trans_case<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, t: Ty<'tcx>, value: /// Set the discriminant for a new value of the given case of the given /// representation. -pub fn trans_set_discr<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr -) { +pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr) { let l = bcx.ccx.layout_of(t); match *l { layout::CEnum{ discr, min, max, .. } => { assert_discr_in_range(Disr(min), Disr(max), to); bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true), - val); + val, None); } layout::General{ discr, .. } => { bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true), - bcx.struct_gep(val, 0)); + bcx.struct_gep(val, 0), None); } layout::Univariant { .. } | layout::UntaggedUnion { .. } @@ -458,7 +401,7 @@ pub fn trans_set_discr<'a, 'tcx>( let nnty = compute_fields(bcx.ccx, t, nndiscr as usize, false)[0]; if to.0 != nndiscr { let llptrty = type_of::sizing_type_of(bcx.ccx, nnty); - bcx.store(C_null(llptrty), val); + bcx.store(C_null(llptrty), val, None); } } layout::StructWrappedNullablePointer { nndiscr, ref discrfield, ref nonnull, .. } => { @@ -476,7 +419,7 @@ pub fn trans_set_discr<'a, 'tcx>( let path = discrfield.iter().map(|&i| i as usize).collect::>(); let llptrptr = bcx.gepi(val, &path[..]); let llptrty = val_ty(llptrptr).element_type(); - bcx.store(C_null(llptrty), llptrptr); + bcx.store(C_null(llptrty), llptrptr, None); } } } @@ -484,11 +427,11 @@ pub fn trans_set_discr<'a, 'tcx>( } } -fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>) -> bool { +fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &Builder<'a, 'tcx>) -> bool { bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64" } -fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) { +pub fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) { if min <= max { assert!(min <= discr && discr <= max) } else { @@ -496,303 +439,6 @@ fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) { } } -/// Access a field, at a point when the value's case is known. -pub fn trans_field_ptr<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, - t: Ty<'tcx>, - val: MaybeSizedValue, - discr: Disr, - ix: usize -) -> ValueRef { - let l = bcx.ccx.layout_of(t); - debug!("trans_field_ptr on {} represented as {:#?}", t, l); - // Note: if this ever needs to generate conditionals (e.g., if we - // decide to do some kind of cdr-coding-like non-unique repr - // someday), it will need to return a possibly-new bcx as well. - match *l { - layout::Univariant { ref variant, .. } => { - assert_eq!(discr, Disr(0)); - struct_field_ptr(bcx, &variant, - &compute_fields(bcx.ccx, t, 0, false), - val, ix, false) - } - layout::Vector { count, .. } => { - assert_eq!(discr.0, 0); - assert!((ix as u64) < count); - bcx.struct_gep(val.value, ix) - } - layout::General { discr: d, ref variants, .. } => { - let mut fields = compute_fields(bcx.ccx, t, discr.0 as usize, false); - fields.insert(0, d.to_ty(&bcx.ccx.tcx(), false)); - struct_field_ptr(bcx, &variants[discr.0 as usize], - &fields, - val, ix + 1, true) - } - layout::UntaggedUnion { .. } => { - let fields = compute_fields(bcx.ccx, t, 0, false); - let ty = type_of::in_memory_type_of(bcx.ccx, fields[ix]); - bcx.pointercast(val.value, ty.ptr_to()) - } - layout::RawNullablePointer { nndiscr, .. } | - layout::StructWrappedNullablePointer { nndiscr, .. } if discr.0 != nndiscr => { - let nullfields = compute_fields(bcx.ccx, t, (1-nndiscr) as usize, false); - // The unit-like case might have a nonzero number of unit-like fields. - // (e.d., Result of Either with (), as one side.) - let ty = type_of::type_of(bcx.ccx, nullfields[ix]); - assert_eq!(machine::llsize_of_alloc(bcx.ccx, ty), 0); - bcx.pointercast(val.value, ty.ptr_to()) - } - layout::RawNullablePointer { nndiscr, .. } => { - let nnty = compute_fields(bcx.ccx, t, nndiscr as usize, false)[0]; - assert_eq!(ix, 0); - assert_eq!(discr.0, nndiscr); - let ty = type_of::type_of(bcx.ccx, nnty); - bcx.pointercast(val.value, ty.ptr_to()) - } - layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { - assert_eq!(discr.0, nndiscr); - struct_field_ptr(bcx, &nonnull, - &compute_fields(bcx.ccx, t, discr.0 as usize, false), - val, ix, false) - } - _ => bug!("element access in type without elements: {} represented as {:#?}", t, l) - } -} - -fn struct_field_ptr<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, - st: &layout::Struct, - fields: &Vec>, - val: MaybeSizedValue, - ix: usize, - needs_cast: bool -) -> ValueRef { - let fty = fields[ix]; - let ccx = bcx.ccx; - - let ptr_val = if needs_cast { - let fields = st.field_index_by_increasing_offset().map(|i| { - type_of::in_memory_type_of(ccx, fields[i]) - }).collect::>(); - let real_ty = Type::struct_(ccx, &fields[..], st.packed); - bcx.pointercast(val.value, real_ty.ptr_to()) - } else { - val.value - }; - - // Simple case - we can just GEP the field - // * First field - Always aligned properly - // * Packed struct - There is no alignment padding - // * Field is sized - pointer is properly aligned already - if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed || - bcx.ccx.shared().type_is_sized(fty) { - return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize); - } - - // If the type of the last field is [T] or str, then we don't need to do - // any adjusments - match fty.sty { - ty::TySlice(..) | ty::TyStr => { - return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize); - } - _ => () - } - - // There's no metadata available, log the case and just do the GEP. - if !val.has_meta() { - debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment", - ix, Value(ptr_val)); - return bcx.struct_gep(ptr_val, ix); - } - - // We need to get the pointer manually now. - // We do this by casting to a *i8, then offsetting it by the appropriate amount. - // We do this instead of, say, simply adjusting the pointer from the result of a GEP - // because the field may have an arbitrary alignment in the LLVM representation - // anyway. - // - // To demonstrate: - // struct Foo { - // x: u16, - // y: T - // } - // - // The type Foo> is represented in LLVM as { u16, { u16, u8 }}, meaning that - // the `y` field has 16-bit alignment. - - let meta = val.meta; - - - let offset = st.offsets[ix].bytes(); - let unaligned_offset = C_uint(bcx.ccx, offset); - - // Get the alignment of the field - let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta); - - // Bump the unaligned offset up to the appropriate alignment using the - // following expression: - // - // (unaligned offset + (align - 1)) & -align - - // Calculate offset - let align_sub_1 = bcx.sub(align, C_uint(bcx.ccx, 1u64)); - let offset = bcx.and(bcx.add(unaligned_offset, align_sub_1), - bcx.neg(align)); - - debug!("struct_field_ptr: DST field offset: {:?}", Value(offset)); - - // Cast and adjust pointer - let byte_ptr = bcx.pointercast(ptr_val, Type::i8p(bcx.ccx)); - let byte_ptr = bcx.gep(byte_ptr, &[offset]); - - // Finally, cast back to the type expected - let ll_fty = type_of::in_memory_type_of(bcx.ccx, fty); - debug!("struct_field_ptr: Field type is {:?}", ll_fty); - bcx.pointercast(byte_ptr, ll_fty.ptr_to()) -} - -/// Construct a constant value, suitable for initializing a -/// GlobalVariable, given a case and constant values for its fields. -/// Note that this may have a different LLVM type (and different -/// alignment!) from the representation's `type_of`, so it needs a -/// pointer cast before use. -/// -/// The LLVM type system does not directly support unions, and only -/// pointers can be bitcast, so a constant (and, by extension, the -/// GlobalVariable initialized by it) will have a type that can vary -/// depending on which case of an enum it is. -/// -/// To understand the alignment situation, consider `enum E { V64(u64), -/// V32(u32, u32) }` on Windows. The type has 8-byte alignment to -/// accommodate the u64, but `V32(x, y)` would have LLVM type `{i32, -/// i32, i32}`, which is 4-byte aligned. -/// -/// Currently the returned value has the same size as the type, but -/// this could be changed in the future to avoid allocating unnecessary -/// space after values of shorter-than-maximum cases. -pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: Disr, - vals: &[ValueRef]) -> ValueRef { - let l = ccx.layout_of(t); - let dl = &ccx.tcx().data_layout; - match *l { - layout::CEnum { discr: d, min, max, .. } => { - assert_eq!(vals.len(), 0); - assert_discr_in_range(Disr(min), Disr(max), discr); - C_integral(Type::from_integer(ccx, d), discr.0, true) - } - layout::General { discr: d, ref variants, .. } => { - let variant = &variants[discr.0 as usize]; - let lldiscr = C_integral(Type::from_integer(ccx, d), discr.0 as u64, true); - let mut vals_with_discr = vec![lldiscr]; - vals_with_discr.extend_from_slice(vals); - let mut contents = build_const_struct(ccx, &variant, &vals_with_discr[..]); - let needed_padding = l.size(dl).bytes() - variant.stride().bytes(); - if needed_padding > 0 { - contents.push(padding(ccx, needed_padding)); - } - C_struct(ccx, &contents[..], false) - } - layout::UntaggedUnion { ref variants, .. }=> { - assert_eq!(discr, Disr(0)); - let contents = build_const_union(ccx, variants, vals[0]); - C_struct(ccx, &contents, variants.packed) - } - layout::Univariant { ref variant, .. } => { - assert_eq!(discr, Disr(0)); - let contents = build_const_struct(ccx, &variant, vals); - C_struct(ccx, &contents[..], variant.packed) - } - layout::Vector { .. } => { - C_vector(vals) - } - layout::RawNullablePointer { nndiscr, .. } => { - let nnty = compute_fields(ccx, t, nndiscr as usize, false)[0]; - if discr.0 == nndiscr { - assert_eq!(vals.len(), 1); - vals[0] - } else { - C_null(type_of::sizing_type_of(ccx, nnty)) - } - } - layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { - if discr.0 == nndiscr { - C_struct(ccx, &build_const_struct(ccx, &nonnull, vals), false) - } else { - let fields = compute_fields(ccx, t, nndiscr as usize, false); - let vals = fields.iter().map(|&ty| { - // Always use null even if it's not the `discrfield`th - // field; see #8506. - C_null(type_of::sizing_type_of(ccx, ty)) - }).collect::>(); - C_struct(ccx, &build_const_struct(ccx, &nonnull, &vals[..]), false) - } - } - _ => bug!("trans_const: cannot handle type {} repreented as {:#?}", t, l) - } -} - -/// Building structs is a little complicated, because we might need to -/// insert padding if a field's value is less aligned than its type. -/// -/// Continuing the example from `trans_const`, a value of type `(u32, -/// E)` should have the `E` at offset 8, but if that field's -/// initializer is 4-byte aligned then simply translating the tuple as -/// a two-element struct will locate it at offset 4, and accesses to it -/// will read the wrong memory. -fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - st: &layout::Struct, - vals: &[ValueRef]) - -> Vec { - assert_eq!(vals.len(), st.offsets.len()); - - if vals.len() == 0 { - return Vec::new(); - } - - // offset of current value - let mut offset = 0; - let mut cfields = Vec::new(); - cfields.reserve(st.offsets.len()*2); - - let parts = st.field_index_by_increasing_offset().map(|i| { - (&vals[i], st.offsets[i].bytes()) - }); - for (&val, target_offset) in parts { - if offset < target_offset { - cfields.push(padding(ccx, target_offset - offset)); - offset = target_offset; - } - assert!(!is_undef(val)); - cfields.push(val); - offset += machine::llsize_of_alloc(ccx, val_ty(val)); - } - - if offset < st.stride().bytes() { - cfields.push(padding(ccx, st.stride().bytes() - offset)); - } - - cfields -} - -fn build_const_union<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - un: &layout::Union, - field_val: ValueRef) - -> Vec { - let mut cfields = vec![field_val]; - - let offset = machine::llsize_of_alloc(ccx, val_ty(field_val)); - let size = un.stride().bytes(); - if offset != size { - cfields.push(padding(ccx, size - offset)); - } - - cfields -} - -fn padding(ccx: &CrateContext, size: u64) -> ValueRef { - C_undef(Type::array(&Type::i8(ccx), size)) -} - // FIXME this utility routine should be somewhere more general #[inline] fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a } diff --git a/src/librustc_trans/asm.rs b/src/librustc_trans/asm.rs index d6385e1ca156..c95d41470187 100644 --- a/src/librustc_trans/asm.rs +++ b/src/librustc_trans/asm.rs @@ -15,6 +15,7 @@ use base; use common::*; use type_of; use type_::Type; +use builder::Builder; use rustc::hir; use rustc::ty::Ty; @@ -25,7 +26,7 @@ use libc::{c_uint, c_char}; // Take an inline assembly expression and splat it out via LLVM pub fn trans_inline_asm<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, ia: &hir::InlineAsm, outputs: Vec<(ValueRef, Ty<'tcx>)>, mut inputs: Vec @@ -105,7 +106,7 @@ pub fn trans_inline_asm<'a, 'tcx>( let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); for (i, (_, &(val, _))) in outputs.enumerate() { let v = if num_outputs == 1 { r } else { bcx.extract_value(r, i) }; - bcx.store(v, val); + bcx.store(v, val, None); } // Store expn_id in a metadata node so we can map LLVM errors diff --git a/src/librustc_trans/back/rpath.rs b/src/librustc_trans/back/rpath.rs index ccaa0d4e1b1b..9c982be3fa03 100644 --- a/src/librustc_trans/back/rpath.rs +++ b/src/librustc_trans/back/rpath.rs @@ -51,7 +51,13 @@ pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec { fn rpaths_to_flags(rpaths: &[String]) -> Vec { let mut ret = Vec::new(); for rpath in rpaths { - ret.push(format!("-Wl,-rpath,{}", &(*rpath))); + if rpath.contains(',') { + ret.push("-Wl,-rpath".into()); + ret.push("-Xlinker".into()); + ret.push(rpath.clone()); + } else { + ret.push(format!("-Wl,-rpath,{}", &(*rpath))); + } } return ret; } @@ -258,4 +264,19 @@ mod tests { assert_eq!(res, "$ORIGIN/../lib"); } } + + #[test] + fn test_xlinker() { + let args = rpaths_to_flags(&[ + "a/normal/path".to_string(), + "a,comma,path".to_string() + ]); + + assert_eq!(args, vec![ + "-Wl,-rpath,a/normal/path".to_string(), + "-Wl,-rpath".to_string(), + "-Xlinker".to_string(), + "a,comma,path".to_string() + ]); + } } diff --git a/src/librustc_trans/back/symbol_names.rs b/src/librustc_trans/back/symbol_names.rs index 938848054fee..8f8c48a06b2c 100644 --- a/src/librustc_trans/back/symbol_names.rs +++ b/src/librustc_trans/back/symbol_names.rs @@ -152,6 +152,15 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, assert!(!substs.has_erasable_regions()); assert!(!substs.needs_subst()); substs.visit_with(&mut hasher); + + // If this is an instance of a generic function, we also hash in + // the ID of the instantiating crate. This avoids symbol conflicts + // in case the same instances is emitted in two crates of the same + // project. + if substs.types().next().is_some() { + hasher.hash(scx.tcx().crate_name.as_str()); + hasher.hash(scx.sess().local_crate_disambiguator().as_str()); + } } }); diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 47b76658bdd1..4cdde24ed48b 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -37,8 +37,9 @@ use llvm; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use middle::lang_items::StartFnLangItem; use rustc::ty::subst::Substs; +use rustc::mir::tcx::LvalueTy; use rustc::traits; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; use rustc::dep_graph::{DepNode, WorkProduct}; use rustc::hir::map as hir_map; @@ -47,14 +48,15 @@ use session::config::{self, NoDebugInfo}; use rustc_incremental::IncrementalHashesMap; use session::{self, DataTypeKind, Session}; use abi::{self, Abi, FnType}; +use mir::lvalue::LvalueRef; use adt; use attributes; use builder::Builder; use callee::{Callee}; -use common::{BlockAndBuilder, C_bool, C_bytes_in_context, C_i32, C_uint}; +use common::{C_bool, C_bytes_in_context, C_i32, C_uint}; use collector::{self, TransItemCollectionMode}; use common::{C_struct_in_context, C_u64, C_undef}; -use common::{CrateContext, FunctionContext}; +use common::CrateContext; use common::{fulfill_obligation}; use common::{type_is_zero_size, val_ty}; use common; @@ -161,7 +163,7 @@ pub fn bin_op_to_fcmp_predicate(op: hir::BinOp_) -> llvm::RealPredicate { } pub fn compare_simd_types<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, lhs: ValueRef, rhs: ValueRef, t: Ty<'tcx>, @@ -218,7 +220,7 @@ pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>, /// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. pub fn unsize_thin_ptr<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, src: ValueRef, src_ty: Ty<'tcx>, dst_ty: Ty<'tcx> @@ -242,7 +244,7 @@ pub fn unsize_thin_ptr<'a, 'tcx>( /// Coerce `src`, which is a reference to a value of type `src_ty`, /// to a value of type `dst_ty` and store the result in `dst` -pub fn coerce_unsized_into<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, +pub fn coerce_unsized_into<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, src: ValueRef, src_ty: Ty<'tcx>, dst: ValueRef, @@ -278,8 +280,8 @@ pub fn coerce_unsized_into<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, monomorphize::field_ty(bcx.tcx(), substs_b, f) }); - let src = adt::MaybeSizedValue::sized(src); - let dst = adt::MaybeSizedValue::sized(dst); + let src = LvalueRef::new_sized_ty(src, src_ty); + let dst = LvalueRef::new_sized_ty(dst, dst_ty); let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_fty, dst_fty)) in iter { @@ -287,10 +289,10 @@ pub fn coerce_unsized_into<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, continue; } - let src_f = adt::trans_field_ptr(bcx, src_ty, src, Disr(0), i); - let dst_f = adt::trans_field_ptr(bcx, dst_ty, dst, Disr(0), i); + let src_f = src.trans_field_ptr(bcx, i); + let dst_f = dst.trans_field_ptr(bcx, i); if src_fty == dst_fty { - memcpy_ty(bcx, dst_f, src_f, src_fty); + memcpy_ty(bcx, dst_f, src_f, src_fty, None); } else { coerce_unsized_into(bcx, src_f, src_fty, dst_f, dst_fty); } @@ -322,7 +324,7 @@ pub fn custom_coerce_unsize_info<'scx, 'tcx>(scx: &SharedCrateContext<'scx, 'tcx } pub fn cast_shift_expr_rhs( - cx: &BlockAndBuilder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef + cx: &Builder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef ) -> ValueRef { cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b)) } @@ -421,7 +423,7 @@ pub fn load_ty<'a, 'tcx>(b: &Builder<'a, 'tcx>, ptr: ValueRef, t: Ty<'tcx>) -> V /// Helper for storing values in memory. Does the necessary conversion if the in-memory type /// differs from the type used for SSA values. -pub fn store_ty<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>, v: ValueRef, dst: ValueRef, t: Ty<'tcx>) { +pub fn store_ty<'a, 'tcx>(cx: &Builder<'a, 'tcx>, v: ValueRef, dst: ValueRef, t: Ty<'tcx>) { debug!("store_ty: {:?} : {:?} <- {:?}", Value(dst), t, Value(v)); if common::type_is_fat_ptr(cx.ccx, t) { @@ -429,18 +431,18 @@ pub fn store_ty<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>, v: ValueRef, dst: Valu let llextra = cx.extract_value(v, abi::FAT_PTR_EXTRA); store_fat_ptr(cx, lladdr, llextra, dst, t); } else { - cx.store(from_immediate(cx, v), dst); + cx.store(from_immediate(cx, v), dst, None); } } -pub fn store_fat_ptr<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>, +pub fn store_fat_ptr<'a, 'tcx>(cx: &Builder<'a, 'tcx>, data: ValueRef, extra: ValueRef, dst: ValueRef, _ty: Ty<'tcx>) { // FIXME: emit metadata - cx.store(data, get_dataptr(cx, dst)); - cx.store(extra, get_meta(cx, dst)); + cx.store(data, get_dataptr(cx, dst), None); + cx.store(extra, get_meta(cx, dst), None); } pub fn load_fat_ptr<'a, 'tcx>( @@ -459,7 +461,7 @@ pub fn load_fat_ptr<'a, 'tcx>( (ptr, meta) } -pub fn from_immediate(bcx: &BlockAndBuilder, val: ValueRef) -> ValueRef { +pub fn from_immediate(bcx: &Builder, val: ValueRef) -> ValueRef { if val_ty(val) == Type::i1(bcx.ccx) { bcx.zext(val, Type::i8(bcx.ccx)) } else { @@ -467,7 +469,7 @@ pub fn from_immediate(bcx: &BlockAndBuilder, val: ValueRef) -> ValueRef { } } -pub fn to_immediate(bcx: &BlockAndBuilder, val: ValueRef, ty: Ty) -> ValueRef { +pub fn to_immediate(bcx: &Builder, val: ValueRef, ty: Ty) -> ValueRef { if ty.is_bool() { bcx.trunc(val, Type::i1(bcx.ccx)) } else { @@ -524,7 +526,11 @@ pub fn call_memcpy<'a, 'tcx>(b: &Builder<'a, 'tcx>, } pub fn memcpy_ty<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, dst: ValueRef, src: ValueRef, t: Ty<'tcx> + bcx: &Builder<'a, 'tcx>, + dst: ValueRef, + src: ValueRef, + t: Ty<'tcx>, + align: Option, ) { let ccx = bcx.ccx; @@ -532,17 +538,10 @@ pub fn memcpy_ty<'a, 'tcx>( return; } - if t.is_structural() { - let llty = type_of::type_of(ccx, t); - let llsz = llsize_of(ccx, llty); - let llalign = type_of::align_of(ccx, t); - call_memcpy(bcx, dst, src, llsz, llalign as u32); - } else if common::type_is_fat_ptr(bcx.ccx, t) { - let (data, extra) = load_fat_ptr(bcx, src, t); - store_fat_ptr(bcx, data, extra, dst, t); - } else { - store_ty(bcx, load_ty(bcx, src, t), dst, t); - } + let llty = type_of::type_of(ccx, t); + let llsz = llsize_of(ccx, llty); + let llalign = align.unwrap_or_else(|| type_of::align_of(ccx, t)); + call_memcpy(bcx, dst, src, llsz, llalign as u32); } pub fn call_memset<'a, 'tcx>(b: &Builder<'a, 'tcx>, @@ -558,11 +557,6 @@ pub fn call_memset<'a, 'tcx>(b: &Builder<'a, 'tcx>, b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None) } -pub fn alloc_ty<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> ValueRef { - assert!(!ty.has_param_types()); - bcx.fcx().alloca(type_of::type_of(bcx.ccx, ty), name) -} - pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) { let _s = if ccx.sess().trans_stats() { let mut instance_name = String::new(); @@ -598,18 +592,17 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance let fn_ty = FnType::new(ccx, abi, &sig, &[]); - let fcx = FunctionContext::new(ccx, lldecl); let mir = ccx.tcx().item_mir(instance.def); - mir::trans_mir(&fcx, fn_ty, &mir, instance, &sig, abi); + mir::trans_mir(ccx, lldecl, fn_ty, &mir, instance, &sig, abi); } pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, disr: Disr, - llfndecl: ValueRef) { - attributes::inline(llfndecl, attributes::InlineAttr::Hint); - attributes::set_frame_pointer_elimination(ccx, llfndecl); + llfn: ValueRef) { + attributes::inline(llfn, attributes::InlineAttr::Hint); + attributes::set_frame_pointer_elimination(ccx, llfn); let ctor_ty = ccx.tcx().item_type(def_id); let ctor_ty = monomorphize::apply_param_substs(ccx.shared(), substs, &ctor_ty); @@ -617,24 +610,29 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&ctor_ty.fn_sig()); let fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]); - let fcx = FunctionContext::new(ccx, llfndecl); - let bcx = fcx.get_entry_block(); + let bcx = Builder::new_block(ccx, llfn, "entry-block"); if !fn_ty.ret.is_ignore() { // But if there are no nested returns, we skip the indirection // and have a single retslot let dest = if fn_ty.ret.is_indirect() { - get_param(fcx.llfn, 0) + get_param(llfn, 0) } else { // We create an alloca to hold a pointer of type `ret.original_ty` // which will hold the pointer to the right alloca which has the // final ret value - fcx.alloca(fn_ty.ret.memory_ty(ccx), "sret_slot") + bcx.alloca(fn_ty.ret.memory_ty(ccx), "sret_slot") + }; + // Can return unsized value + let mut dest_val = LvalueRef::new_sized_ty(dest, sig.output()); + dest_val.ty = LvalueTy::Downcast { + adt_def: sig.output().ty_adt_def().unwrap(), + substs: substs, + variant_index: disr.0 as usize, }; - let dest_val = adt::MaybeSizedValue::sized(dest); // Can return unsized value let mut llarg_idx = fn_ty.ret.is_indirect() as usize; let mut arg_idx = 0; for (i, arg_ty) in sig.inputs().iter().enumerate() { - let lldestptr = adt::trans_field_ptr(&bcx, sig.output(), dest_val, Disr::from(disr), i); + let lldestptr = dest_val.trans_field_ptr(&bcx, i); let arg = &fn_ty.args[arg_idx]; arg_idx += 1; if common::type_is_fat_ptr(bcx.ccx, arg_ty) { @@ -761,12 +759,7 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { // `main` should respect same config for frame pointer elimination as rest of code attributes::set_frame_pointer_elimination(ccx, llfn); - let llbb = unsafe { - let name = CString::new("top").unwrap(); - llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, name.as_ptr()) - }; - let bld = Builder::with_ccx(ccx); - bld.position_at_end(llbb); + let bld = Builder::new_block(ccx, llfn, "top"); debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(ccx, &bld); diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index 136d1aad31a0..cf7f3e9501d1 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -19,12 +19,17 @@ use machine::llalign_of_pref; use type_::Type; use value::Value; use libc::{c_uint, c_char}; +use rustc::ty::{Ty, TyCtxt, TypeFoldable}; +use rustc::session::Session; +use type_of; use std::borrow::Cow; use std::ffi::CString; use std::ptr; use syntax_pos::Span; +// All Builders must have an llfn associated with them +#[must_use] pub struct Builder<'a, 'tcx: 'a> { pub llbuilder: BuilderRef, pub ccx: &'a CrateContext<'a, 'tcx>, @@ -46,6 +51,20 @@ fn noname() -> *const c_char { } impl<'a, 'tcx> Builder<'a, 'tcx> { + pub fn new_block<'b>(ccx: &'a CrateContext<'a, 'tcx>, llfn: ValueRef, name: &'b str) -> Self { + let builder = Builder::with_ccx(ccx); + let llbb = unsafe { + let name = CString::new(name).unwrap(); + llvm::LLVMAppendBasicBlockInContext( + ccx.llcx(), + llfn, + name.as_ptr() + ) + }; + builder.position_at_end(llbb); + builder + } + pub fn with_ccx(ccx: &'a CrateContext<'a, 'tcx>) -> Self { // Create a fresh builder from the crate context. let llbuilder = unsafe { @@ -57,6 +76,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + pub fn build_sibling_block<'b>(&self, name: &'b str) -> Builder<'a, 'tcx> { + Builder::new_block(self.ccx, self.llfn(), name) + } + + pub fn sess(&self) -> &Session { + self.ccx.sess() + } + + pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.ccx.tcx() + } + + pub fn llfn(&self) -> ValueRef { + unsafe { + llvm::LLVMGetBasicBlockParent(self.llbb()) + } + } + + pub fn llbb(&self) -> BasicBlockRef { + unsafe { + llvm::LLVMGetInsertBlock(self.llbuilder) + } + } + fn count_insn(&self, category: &str) { if self.ccx.sess().trans_stats() { self.ccx.stats().n_llvm_insns.set(self.ccx.stats().n_llvm_insns.get() + 1); @@ -435,6 +478,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + pub fn alloca(&self, ty: Type, name: &str) -> ValueRef { + let builder = Builder::with_ccx(self.ccx); + builder.position_at_start(unsafe { + llvm::LLVMGetFirstBasicBlock(self.llfn()) + }); + builder.dynamic_alloca(ty, name) + } + + pub fn alloca_ty(&self, ty: Ty<'tcx>, name: &str) -> ValueRef { + assert!(!ty.has_param_types()); + self.alloca(type_of::type_of(self.ccx, ty), name) + } + pub fn dynamic_alloca(&self, ty: Type, name: &str) -> ValueRef { self.count_insn("alloca"); unsafe { @@ -512,13 +568,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { value } - pub fn store(&self, val: ValueRef, ptr: ValueRef) -> ValueRef { + pub fn store(&self, val: ValueRef, ptr: ValueRef, align: Option) -> ValueRef { debug!("Store {:?} -> {:?}", Value(val), Value(ptr)); assert!(!self.llbuilder.is_null()); self.count_insn("store"); let ptr = self.check_store(val, ptr); unsafe { - llvm::LLVMBuildStore(self.llbuilder, val, ptr) + let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + if let Some(align) = align { + llvm::LLVMSetAlignment(store, align as c_uint); + } + store } } diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 1abe25ea6073..58d0c4685035 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -23,11 +23,10 @@ use rustc::traits; use abi::{Abi, FnType}; use attributes; use base; -use base::*; -use common::{ - self, CrateContext, FunctionContext, SharedCrateContext -}; -use adt::MaybeSizedValue; +use builder::Builder; +use common::{self, CrateContext, SharedCrateContext}; +use cleanup::CleanupScope; +use mir::lvalue::LvalueRef; use consts; use declare; use value::Value; @@ -235,18 +234,37 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, trait_closure_kind={:?}, llfn={:?})", llfn_closure_kind, trait_closure_kind, Value(llfn)); - match (llfn_closure_kind, trait_closure_kind) { + match needs_fn_once_adapter_shim(llfn_closure_kind, trait_closure_kind) { + Ok(true) => trans_fn_once_adapter_shim(ccx, + def_id, + substs, + method_instance, + llfn), + Ok(false) => llfn, + Err(()) => { + bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}", + llfn_closure_kind, + trait_closure_kind); + } + } +} + +pub fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, + trait_closure_kind: ty::ClosureKind) + -> Result +{ + match (actual_closure_kind, trait_closure_kind) { (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { // No adapter needed. - llfn + Ok(false) } (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { // The closure fn `llfn` is a `fn(&self, ...)`. We want a // `fn(&mut self, ...)`. In fact, at trans time, these are // basically the same thing, so we can just return llfn. - llfn + Ok(false) } (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { @@ -258,13 +276,9 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, // fn call_once(mut self, ...) { call_mut(&mut self, ...) } // // These are both the same at trans time. - trans_fn_once_adapter_shim(ccx, def_id, substs, method_instance, llfn) - } - _ => { - bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}", - llfn_closure_kind, - trait_closure_kind); + Ok(true) } + _ => Err(()), } } @@ -330,8 +344,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( attributes::set_frame_pointer_elimination(ccx, lloncefn); let orig_fn_ty = fn_ty; - let fcx = FunctionContext::new(ccx, lloncefn); - let mut bcx = fcx.get_entry_block(); + let mut bcx = Builder::new_block(ccx, lloncefn, "entry-block"); let callee = Callee { data: Fn(llreffn), @@ -340,7 +353,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // the first argument (`self`) will be the (by value) closure env. - let mut llargs = get_params(fcx.llfn); + let mut llargs = get_params(lloncefn); let fn_ret = callee.ty.fn_ret(); let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); let self_idx = fn_ty.ret.is_indirect() as usize; @@ -348,7 +361,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let llenv = if env_arg.is_indirect() { llargs[self_idx] } else { - let scratch = alloc_ty(&bcx, closure_ty, "self"); + let scratch = bcx.alloca_ty(closure_ty, "self"); let mut llarg_idx = self_idx; env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch); scratch @@ -365,12 +378,14 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // Call the by-ref closure body with `self` in a cleanup scope, // to drop `self` when the body returns, or in case it unwinds. - let self_scope = fcx.schedule_drop_mem(MaybeSizedValue::sized(llenv), closure_ty); + let self_scope = CleanupScope::schedule_drop_mem( + &bcx, LvalueRef::new_sized_ty(llenv, closure_ty) + ); let llfn = callee.reify(bcx.ccx); let llret; if let Some(landing_pad) = self_scope.landing_pad { - let normal_bcx = bcx.fcx().build_new_block("normal-return"); + let normal_bcx = bcx.build_sibling_block("normal-return"); llret = bcx.invoke(llfn, &llargs[..], normal_bcx.llbb(), landing_pad, None); bcx = normal_bcx; } else { @@ -489,10 +504,9 @@ fn trans_fn_pointer_shim<'a, 'tcx>( let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty); attributes::set_frame_pointer_elimination(ccx, llfn); // - let fcx = FunctionContext::new(ccx, llfn); - let bcx = fcx.get_entry_block(); + let bcx = Builder::new_block(ccx, llfn, "entry-block"); - let mut llargs = get_params(fcx.llfn); + let mut llargs = get_params(llfn); let self_arg = llargs.remove(fn_ty.ret.is_indirect() as usize); let llfnpointer = llfnpointer.unwrap_or_else(|| { diff --git a/src/librustc_trans/cleanup.rs b/src/librustc_trans/cleanup.rs index 4e59ea3f6c5e..5d89a67d3fd8 100644 --- a/src/librustc_trans/cleanup.rs +++ b/src/librustc_trans/cleanup.rs @@ -20,11 +20,12 @@ use llvm::BasicBlockRef; use base; -use adt::MaybeSizedValue; -use common::{BlockAndBuilder, FunctionContext, Funclet}; +use mir::lvalue::LvalueRef; +use rustc::mir::tcx::LvalueTy; +use builder::Builder; +use common::Funclet; use glue; use type_::Type; -use rustc::ty::Ty; pub struct CleanupScope<'tcx> { // Cleanup to run upon scope exit. @@ -36,14 +37,13 @@ pub struct CleanupScope<'tcx> { #[derive(Copy, Clone)] pub struct DropValue<'tcx> { - val: MaybeSizedValue, - ty: Ty<'tcx>, + val: LvalueRef<'tcx>, skip_dtor: bool, } impl<'tcx> DropValue<'tcx> { - fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &BlockAndBuilder<'a, 'tcx>) { - glue::call_drop_glue(bcx, self.val, self.ty, self.skip_dtor, funclet) + fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &Builder<'a, 'tcx>) { + glue::call_drop_glue(bcx, self.val, self.skip_dtor, funclet) } /// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary @@ -52,13 +52,13 @@ impl<'tcx> DropValue<'tcx> { /// landing_pad -> ... cleanups ... -> [resume] /// /// This should only be called once per function, as it creates an alloca for the landingpad. - fn get_landing_pad<'a>(&self, fcx: &FunctionContext<'a, 'tcx>) -> BasicBlockRef { + fn get_landing_pad<'a>(&self, bcx: &Builder<'a, 'tcx>) -> BasicBlockRef { debug!("get_landing_pad"); - let bcx = fcx.build_new_block("cleanup_unwind"); + let bcx = bcx.build_sibling_block("cleanup_unwind"); let llpersonality = bcx.ccx.eh_personality(); bcx.set_personality_fn(llpersonality); - if base::wants_msvc_seh(fcx.ccx.sess()) { + if base::wants_msvc_seh(bcx.sess()) { let pad = bcx.cleanup_pad(None, &[]); let funclet = Some(Funclet::new(pad)); self.trans(funclet.as_ref(), &bcx); @@ -68,10 +68,10 @@ impl<'tcx> DropValue<'tcx> { // The landing pad return type (the type being propagated). Not sure // what this represents but it's determined by the personality // function and this is what the EH proposal example uses. - let llretty = Type::struct_(fcx.ccx, &[Type::i8p(fcx.ccx), Type::i32(fcx.ccx)], false); + let llretty = Type::struct_(bcx.ccx, &[Type::i8p(bcx.ccx), Type::i32(bcx.ccx)], false); // The only landing pad clause will be 'cleanup' - let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.fcx().llfn); + let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.llfn()); // The landing pad block is a cleanup bcx.set_cleanup(llretval); @@ -92,17 +92,23 @@ impl<'tcx> DropValue<'tcx> { } } -impl<'a, 'tcx> FunctionContext<'a, 'tcx> { +impl<'a, 'tcx> CleanupScope<'tcx> { /// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty` - pub fn schedule_drop_mem(&self, val: MaybeSizedValue, ty: Ty<'tcx>) -> CleanupScope<'tcx> { - if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); } + pub fn schedule_drop_mem( + bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx> + ) -> CleanupScope<'tcx> { + if let LvalueTy::Downcast { .. } = val.ty { + bug!("Cannot drop downcast ty yet"); + } + if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) { + return CleanupScope::noop(); + } let drop = DropValue { val: val, - ty: ty, skip_dtor: false, }; - CleanupScope::new(self, drop) + CleanupScope::new(bcx, drop) } /// Issue #23611: Schedules a (deep) drop of the contents of @@ -110,28 +116,31 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { /// `ty`. The scheduled code handles extracting the discriminant /// and dropping the contents associated with that variant /// *without* executing any associated drop implementation. - pub fn schedule_drop_adt_contents(&self, val: MaybeSizedValue, ty: Ty<'tcx>) - -> CleanupScope<'tcx> { + pub fn schedule_drop_adt_contents( + bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx> + ) -> CleanupScope<'tcx> { + if let LvalueTy::Downcast { .. } = val.ty { + bug!("Cannot drop downcast ty yet"); + } // `if` below could be "!contents_needs_drop"; skipping drop // is just an optimization, so sound to be conservative. - if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); } + if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) { + return CleanupScope::noop(); + } let drop = DropValue { val: val, - ty: ty, skip_dtor: true, }; - CleanupScope::new(self, drop) + CleanupScope::new(bcx, drop) } -} -impl<'tcx> CleanupScope<'tcx> { - fn new<'a>(fcx: &FunctionContext<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> { + fn new(bcx: &Builder<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> { CleanupScope { cleanup: Some(drop_val), - landing_pad: if !fcx.ccx.sess().no_landing_pads() { - Some(drop_val.get_landing_pad(fcx)) + landing_pad: if !bcx.sess().no_landing_pads() { + Some(drop_val.get_landing_pad(bcx)) } else { None }, @@ -145,7 +154,7 @@ impl<'tcx> CleanupScope<'tcx> { } } - pub fn trans<'a>(self, bcx: &'a BlockAndBuilder<'a, 'tcx>) { + pub fn trans(self, bcx: &'a Builder<'a, 'tcx>) { if let Some(cleanup) = self.cleanup { cleanup.trans(None, &bcx); } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 2bc42a461528..5e409a2aa552 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -205,13 +205,14 @@ use rustc::mir::visit::Visitor as MirVisitor; use syntax::abi::Abi; use syntax_pos::DUMMY_SP; use base::custom_coerce_unsize_info; +use callee::needs_fn_once_adapter_shim; use context::SharedCrateContext; use common::fulfill_obligation; use glue::{self, DropGlueKind}; use monomorphize::{self, Instance}; use util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; -use trans_item::{TransItem, DefPathBasedNames}; +use trans_item::{TransItem, DefPathBasedNames, InstantiationMode}; use std::iter; @@ -336,6 +337,10 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, } TransItem::Static(node_id) => { let def_id = scx.tcx().map.local_def_id(node_id); + + // Sanity check whether this ended up being collected accidentally + debug_assert!(should_trans_locally(scx.tcx(), def_id)); + let ty = scx.tcx().item_type(def_id); let ty = glue::get_drop_glue_type(scx, ty); neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); @@ -345,6 +350,9 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>, collect_neighbours(scx, Instance::mono(scx, def_id), &mut neighbors); } TransItem::Fn(instance) => { + // Sanity check whether this ended up being collected accidentally + debug_assert!(should_trans_locally(scx.tcx(), instance.def)); + // Keep track of the monomorphization recursion depth recursion_depth_reset = Some(check_recursion_limit(scx.tcx(), instance, @@ -373,7 +381,7 @@ fn record_inlining_canditates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, callees: &[TransItem<'tcx>], inlining_map: &mut InliningMap<'tcx>) { let is_inlining_candidate = |trans_item: &TransItem<'tcx>| { - trans_item.needs_local_copy(tcx) + trans_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy }; let inlining_candidates = callees.into_iter() @@ -489,15 +497,16 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { .require(ExchangeMallocFnLangItem) .unwrap_or_else(|e| self.scx.sess().fatal(&e)); - assert!(can_have_local_instance(self.scx.tcx(), exchange_malloc_fn_def_id)); - let empty_substs = self.scx.empty_substs_for_def_id(exchange_malloc_fn_def_id); - let exchange_malloc_fn_trans_item = - create_fn_trans_item(self.scx, - exchange_malloc_fn_def_id, - empty_substs, - self.param_substs); + if should_trans_locally(self.scx.tcx(), exchange_malloc_fn_def_id) { + let empty_substs = self.scx.empty_substs_for_def_id(exchange_malloc_fn_def_id); + let exchange_malloc_fn_trans_item = + create_fn_trans_item(self.scx, + exchange_malloc_fn_def_id, + empty_substs, + self.param_substs); - self.output.push(exchange_malloc_fn_trans_item); + self.output.push(exchange_malloc_fn_trans_item); + } } _ => { /* not interesting */ } } @@ -568,7 +577,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { callee_substs, self.param_substs); - if let Some((callee_def_id, callee_substs)) = dispatched { + if let StaticDispatchResult::Dispatched { + def_id: callee_def_id, + substs: callee_substs, + fn_once_adjustment, + } = dispatched { // if we have a concrete impl (which we might not have // in the case of something compiler generated like an // object shim or a closure that is handled differently), @@ -581,6 +594,17 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { callee_substs, self.param_substs); self.output.push(trans_item); + + // This call will instantiate an FnOnce adapter, which drops + // the closure environment. Therefore we need to make sure + // that we collect the drop-glue for the environment type. + if let Some(env_ty) = fn_once_adjustment { + let env_ty = glue::get_drop_glue_type(self.scx, env_ty); + if self.scx.type_needs_drop(env_ty) { + let dg = DropGlueKind::Ty(env_ty); + self.output.push(TransItem::DropGlue(dg)); + } + } } } } @@ -593,7 +617,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { match tcx.item_type(def_id).sty { ty::TyFnDef(def_id, _, f) => { // Some constructors also have type TyFnDef but they are - // always instantiated inline and don't result in + // always instantiated inline and don't result in a // translation item. Same for FFI functions. if let Some(hir_map::NodeForeignItem(_)) = tcx.map.get_if_local(def_id) { return false; @@ -609,7 +633,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { _ => return false } - can_have_local_instance(tcx, def_id) + should_trans_locally(tcx, def_id) } } @@ -659,10 +683,27 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { } } -fn can_have_local_instance<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> bool { - tcx.sess.cstore.can_have_local_instance(tcx, def_id) +// Returns true if we should translate an instance in the local crate. +// Returns false if we can just link to the upstream crate and therefore don't +// need a translation item. +fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> bool { + if def_id.is_local() { + true + } else { + if tcx.sess.cstore.is_exported_symbol(def_id) || + tcx.sess.cstore.is_foreign_item(def_id) { + // We can link to the item in question, no instance needed in this + // crate + false + } else { + if !tcx.sess.cstore.is_item_mir_available(def_id) { + bug!("Cannot create local trans-item for {:?}", def_id) + } + true + } + } } fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, @@ -682,14 +723,15 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // Make sure the BoxFreeFn lang-item gets translated if there is a boxed value. if let ty::TyBox(content_type) = ty.sty { let def_id = scx.tcx().require_lang_item(BoxFreeFnLangItem); - assert!(can_have_local_instance(scx.tcx(), def_id)); - let box_free_fn_trans_item = - create_fn_trans_item(scx, - def_id, - scx.tcx().mk_substs(iter::once(Kind::from(content_type))), - scx.tcx().intern_substs(&[])); - output.push(box_free_fn_trans_item); + if should_trans_locally(scx.tcx(), def_id) { + let box_free_fn_trans_item = + create_fn_trans_item(scx, + def_id, + scx.tcx().mk_substs(iter::once(Kind::from(content_type))), + scx.tcx().intern_substs(&[])); + output.push(box_free_fn_trans_item); + } } // If the type implements Drop, also add a translation item for the @@ -719,7 +761,7 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, _ => bug!() }; - if can_have_local_instance(scx.tcx(), destructor_did) { + if should_trans_locally(scx.tcx(), destructor_did) { let trans_item = create_fn_trans_item(scx, destructor_did, substs, @@ -793,15 +835,13 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, bug!("encountered unexpected type"); } } - - } fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, fn_def_id: DefId, fn_substs: &'tcx Substs<'tcx>, param_substs: &'tcx Substs<'tcx>) - -> Option<(DefId, &'tcx Substs<'tcx>)> { + -> StaticDispatchResult<'tcx> { debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?}, param_substs={:?})", def_id_to_string(scx.tcx(), fn_def_id), fn_substs, @@ -818,10 +858,30 @@ fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, debug!(" => regular function"); // The function is not part of an impl or trait, no dispatching // to be done - Some((fn_def_id, fn_substs)) + StaticDispatchResult::Dispatched { + def_id: fn_def_id, + substs: fn_substs, + fn_once_adjustment: None, + } } } +enum StaticDispatchResult<'tcx> { + // The call could be resolved statically as going to the method with + // `def_id` and `substs`. + Dispatched { + def_id: DefId, + substs: &'tcx Substs<'tcx>, + + // If this is a call to a closure that needs an FnOnce adjustment, + // this contains the new self type of the call (= type of the closure + // environment) + fn_once_adjustment: Option>, + }, + // This goes to somewhere that we don't know at compile-time + Unknown +} + // Given a trait-method and substitution information, find out the actual // implementation of the trait method. fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, @@ -829,7 +889,7 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, trait_id: DefId, callee_substs: &'tcx Substs<'tcx>, param_substs: &'tcx Substs<'tcx>) - -> Option<(DefId, &'tcx Substs<'tcx>)> { + -> StaticDispatchResult<'tcx> { let tcx = scx.tcx(); debug!("do_static_trait_method_dispatch(trait_method={}, \ trait_id={}, \ @@ -850,17 +910,56 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // the actual function: match vtbl { traits::VtableImpl(impl_data) => { - Some(traits::find_method(tcx, trait_method.name, rcvr_substs, &impl_data)) + let (def_id, substs) = traits::find_method(tcx, + trait_method.name, + rcvr_substs, + &impl_data); + StaticDispatchResult::Dispatched { + def_id: def_id, + substs: substs, + fn_once_adjustment: None, + } } traits::VtableClosure(closure_data) => { - Some((closure_data.closure_def_id, closure_data.substs.substs)) + let closure_def_id = closure_data.closure_def_id; + let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + let actual_closure_kind = tcx.closure_kind(closure_def_id); + + let needs_fn_once_adapter_shim = + match needs_fn_once_adapter_shim(actual_closure_kind, + trait_closure_kind) { + Ok(true) => true, + _ => false, + }; + + let fn_once_adjustment = if needs_fn_once_adapter_shim { + Some(tcx.mk_closure_from_closure_substs(closure_def_id, + closure_data.substs)) + } else { + None + }; + + StaticDispatchResult::Dispatched { + def_id: closure_def_id, + substs: closure_data.substs.substs, + fn_once_adjustment: fn_once_adjustment, + } } - // Trait object and function pointer shims are always - // instantiated in-place, and as they are just an ABI-adjusting - // indirect call they do not have any dependencies. - traits::VtableFnPointer(..) | + traits::VtableFnPointer(ref data) => { + // If we know the destination of this fn-pointer, we'll have to make + // sure that this destination actually gets instantiated. + if let ty::TyFnDef(def_id, substs, _) = data.fn_ty.sty { + // The destination of the pointer might be something that needs + // further dispatching, such as a trait method, so we do that. + do_static_dispatch(scx, def_id, substs, param_substs) + } else { + StaticDispatchResult::Unknown + } + } + // Trait object shims are always instantiated in-place, and as they are + // just an ABI-adjusting indirect call they do not have any dependencies. traits::VtableObject(..) => { - None + StaticDispatchResult::Unknown } _ => { bug!("static call to invalid vtable: {:?}", vtbl) @@ -994,9 +1093,20 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a, // Walk all methods of the trait, including those of its supertraits let methods = traits::get_vtable_methods(scx.tcx(), poly_trait_ref); let methods = methods.filter_map(|method| method) - .filter_map(|(def_id, substs)| do_static_dispatch(scx, def_id, substs, - param_substs)) - .filter(|&(def_id, _)| can_have_local_instance(scx.tcx(), def_id)) + .filter_map(|(def_id, substs)| { + if let StaticDispatchResult::Dispatched { + def_id, + substs, + // We already add the drop-glue for the closure env + // unconditionally below. + fn_once_adjustment: _ , + } = do_static_dispatch(scx, def_id, substs, param_substs) { + Some((def_id, substs)) + } else { + None + } + }) + .filter(|&(def_id, _)| should_trans_locally(scx.tcx(), def_id)) .map(|(def_id, substs)| create_fn_trans_item(scx, def_id, substs, param_substs)); output.extend(methods); } @@ -1171,7 +1281,7 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(scx: &SharedCrateContext<'a, ' continue; } - if can_have_local_instance(tcx, method.def_id) { + if should_trans_locally(tcx, method.def_id) { let item = create_fn_trans_item(scx, method.def_id, callee_substs, @@ -1203,6 +1313,7 @@ fn collect_neighbours<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, visitor.visit_mir(&mir); for promoted in &mir.promoted { + visitor.mir = promoted; visitor.visit_mir(promoted); } } diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 7e7bd15dc6e5..419267cb269c 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -12,11 +12,9 @@ //! Code that is useful in various trans modules. -use session::Session; use llvm; -use llvm::{ValueRef, BasicBlockRef, ContextRef, TypeKind}; +use llvm::{ValueRef, ContextRef, TypeKind}; use llvm::{True, False, Bool, OperandBundleDef}; -use rustc::hir::def::Def; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; use rustc::util::common::MemoizationMap; @@ -37,11 +35,9 @@ use rustc::hir; use libc::{c_uint, c_char}; use std::borrow::Cow; use std::iter; -use std::ops::Deref; -use std::ffi::CString; use syntax::ast; -use syntax::symbol::{Symbol, InternedString}; +use syntax::symbol::InternedString; use syntax_pos::Span; use rustc_i128::u128; @@ -49,37 +45,30 @@ use rustc_i128::u128; pub use context::{CrateContext, SharedCrateContext}; pub fn type_is_fat_ptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - match ty.sty { - ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | - ty::TyRef(_, ty::TypeAndMut{ty, ..}) | - ty::TyBox(ty) => { - !ccx.shared().type_is_sized(ty) - } - _ => { - false - } + if let Layout::FatPointer { .. } = *ccx.layout_of(ty) { + true + } else { + false } } pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - use machine::llsize_of_alloc; - use type_of::sizing_type_of; + let layout = ccx.layout_of(ty); + match *layout { + Layout::CEnum { .. } | + Layout::Scalar { .. } | + Layout::Vector { .. } => true, - let simple = ty.is_scalar() || - ty.is_unique() || ty.is_region_ptr() || - ty.is_simd(); - if simple && !type_is_fat_ptr(ccx, ty) { - return true; - } - if !ccx.shared().type_is_sized(ty) { - return false; - } - match ty.sty { - ty::TyAdt(..) | ty::TyTuple(..) | ty::TyArray(..) | ty::TyClosure(..) => { - let llty = sizing_type_of(ccx, ty); - llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type()) + Layout::FatPointer { .. } => false, + + Layout::Array { .. } | + Layout::Univariant { .. } | + Layout::General { .. } | + Layout::UntaggedUnion { .. } | + Layout::RawNullablePointer { .. } | + Layout::StructWrappedNullablePointer { .. } => { + !layout.is_unsized() && layout.size(&ccx.tcx().data_layout).bytes() == 0 } - _ => type_is_zero_size(ccx, ty) } } @@ -172,191 +161,6 @@ pub fn type_is_zero_size<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) - * */ -use Disr; - -/// The concrete version of ty::FieldDef. The name is the field index if -/// the field is numeric. -pub struct Field<'tcx>(pub ast::Name, pub Ty<'tcx>); - -/// The concrete version of ty::VariantDef -pub struct VariantInfo<'tcx> { - pub discr: Disr, - pub fields: Vec> -} - -impl<'a, 'tcx> VariantInfo<'tcx> { - pub fn from_ty(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty: Ty<'tcx>, - opt_def: Option) - -> Self - { - match ty.sty { - ty::TyAdt(adt, substs) => { - let variant = match opt_def { - None => adt.struct_variant(), - Some(def) => adt.variant_of_def(def) - }; - - VariantInfo { - discr: Disr::from(variant.disr_val), - fields: variant.fields.iter().map(|f| { - Field(f.name, monomorphize::field_ty(tcx, substs, f)) - }).collect() - } - } - - ty::TyTuple(ref v) => { - VariantInfo { - discr: Disr(0), - fields: v.iter().enumerate().map(|(i, &t)| { - Field(Symbol::intern(&i.to_string()), t) - }).collect() - } - } - - _ => { - bug!("cannot get field types from the type {:?}", ty); - } - } - } -} - -// Function context. Every LLVM function we create will have one of these. -pub struct FunctionContext<'a, 'tcx: 'a> { - // The ValueRef returned from a call to llvm::LLVMAddFunction; the - // address of the first instruction in the sequence of - // instructions for this function that will go in the .text - // section of the executable we're generating. - pub llfn: ValueRef, - - // A marker for the place where we want to insert the function's static - // allocas, so that LLVM will coalesce them into a single alloca call. - alloca_insert_pt: Option, - - // This function's enclosing crate context. - pub ccx: &'a CrateContext<'a, 'tcx>, - - alloca_builder: Builder<'a, 'tcx>, -} - -impl<'a, 'tcx> FunctionContext<'a, 'tcx> { - /// Create a function context for the given function. - /// Call FunctionContext::get_entry_block for the first entry block. - pub fn new(ccx: &'a CrateContext<'a, 'tcx>, llfndecl: ValueRef) -> FunctionContext<'a, 'tcx> { - let mut fcx = FunctionContext { - llfn: llfndecl, - alloca_insert_pt: None, - ccx: ccx, - alloca_builder: Builder::with_ccx(ccx), - }; - - let val = { - let entry_bcx = fcx.build_new_block("entry-block"); - let val = entry_bcx.load(C_null(Type::i8p(ccx))); - fcx.alloca_builder.position_at_start(entry_bcx.llbb()); - val - }; - - // Use a dummy instruction as the insertion point for all allocas. - // This is later removed in the drop of FunctionContext. - fcx.alloca_insert_pt = Some(val); - - fcx - } - - pub fn get_entry_block(&'a self) -> BlockAndBuilder<'a, 'tcx> { - BlockAndBuilder::new(unsafe { - llvm::LLVMGetFirstBasicBlock(self.llfn) - }, self) - } - - pub fn new_block(&'a self, name: &str) -> BasicBlockRef { - unsafe { - let name = CString::new(name).unwrap(); - llvm::LLVMAppendBasicBlockInContext( - self.ccx.llcx(), - self.llfn, - name.as_ptr() - ) - } - } - - pub fn build_new_block(&'a self, name: &str) -> BlockAndBuilder<'a, 'tcx> { - BlockAndBuilder::new(self.new_block(name), self) - } - - pub fn alloca(&self, ty: Type, name: &str) -> ValueRef { - self.alloca_builder.dynamic_alloca(ty, name) - } -} - -impl<'a, 'tcx> Drop for FunctionContext<'a, 'tcx> { - fn drop(&mut self) { - unsafe { - llvm::LLVMInstructionEraseFromParent(self.alloca_insert_pt.unwrap()); - } - } -} - -#[must_use] -pub struct BlockAndBuilder<'a, 'tcx: 'a> { - // The BasicBlockRef returned from a call to - // llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic - // block to the function pointed to by llfn. We insert - // instructions into that block by way of this block context. - // The block pointing to this one in the function's digraph. - llbb: BasicBlockRef, - - // The function context for the function to which this block is - // attached. - fcx: &'a FunctionContext<'a, 'tcx>, - - builder: Builder<'a, 'tcx>, -} - -impl<'a, 'tcx> BlockAndBuilder<'a, 'tcx> { - pub fn new(llbb: BasicBlockRef, fcx: &'a FunctionContext<'a, 'tcx>) -> Self { - let builder = Builder::with_ccx(fcx.ccx); - // Set the builder's position to this block's end. - builder.position_at_end(llbb); - BlockAndBuilder { - llbb: llbb, - fcx: fcx, - builder: builder, - } - } - - pub fn at_start(&self, f: F) -> R - where F: FnOnce(&BlockAndBuilder<'a, 'tcx>) -> R - { - self.position_at_start(self.llbb); - let r = f(self); - self.position_at_end(self.llbb); - r - } - - pub fn fcx(&self) -> &'a FunctionContext<'a, 'tcx> { - self.fcx - } - pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { - self.ccx.tcx() - } - pub fn sess(&self) -> &'a Session { - self.ccx.sess() - } - - pub fn llbb(&self) -> BasicBlockRef { - self.llbb - } -} - -impl<'a, 'tcx> Deref for BlockAndBuilder<'a, 'tcx> { - type Target = Builder<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.builder - } -} - /// A structure representing an active landing pad for the duration of a basic /// block. /// @@ -654,7 +458,7 @@ pub fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let mut selcx = SelectionContext::new(&infcx); let obligation_cause = traits::ObligationCause::misc(span, @@ -725,7 +529,7 @@ pub fn langcall(tcx: TyCtxt, // of Java. (See related discussion on #1877 and #10183.) pub fn build_unchecked_lshift<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, lhs: ValueRef, rhs: ValueRef ) -> ValueRef { @@ -736,7 +540,7 @@ pub fn build_unchecked_lshift<'a, 'tcx>( } pub fn build_unchecked_rshift<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef + bcx: &Builder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef ) -> ValueRef { let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs); // #1877, #10183: Ensure that input is always valid @@ -749,13 +553,13 @@ pub fn build_unchecked_rshift<'a, 'tcx>( } } -fn shift_mask_rhs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, rhs: ValueRef) -> ValueRef { +fn shift_mask_rhs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, rhs: ValueRef) -> ValueRef { let rhs_llty = val_ty(rhs); bcx.and(rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false)) } pub fn shift_mask_val<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, llty: Type, mask_llty: Type, invert: bool diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index 2e2644d91bb6..a9c1edfdc66c 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -95,8 +95,6 @@ pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { let sym = ccx.symbol_map() .get(TransItem::Static(id)) .expect("Local statics should always be in the SymbolMap"); - // Make sure that this is never executed for something inlined. - assert!(!ccx.tcx().map.is_inlined_node_id(id)); let defined_in_current_codegen_unit = ccx.codegen_unit() .items() diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index 413b64374086..5efdd129a32b 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -851,7 +851,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { } pub fn layout_of(&self, ty: Ty<'tcx>) -> &'tcx ty::layout::Layout { - self.tcx().infer_ctxt(None, None, traits::Reveal::All).enter(|infcx| { + self.tcx().infer_ctxt((), traits::Reveal::All).enter(|infcx| { ty.layout(&infcx).unwrap_or_else(|e| { match e { ty::layout::LayoutError::SizeOverflow(_) => diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs index f5a8eeacf38a..c6f8ba7b6dc7 100644 --- a/src/librustc_trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/debuginfo/create_scope_map.rs @@ -14,7 +14,7 @@ use super::utils::{DIB, span_start}; use llvm; use llvm::debuginfo::{DIScope, DISubprogram}; -use common::{CrateContext, FunctionContext}; +use common::CrateContext; use rustc::mir::{Mir, VisibilityScope}; use libc::c_uint; @@ -44,7 +44,7 @@ impl MirDebugScope { /// Produce DIScope DIEs for each MIR Scope which has variables defined in it. /// If debuginfo is disabled, the returned vector is empty. -pub fn create_mir_scopes(fcx: &FunctionContext, mir: &Mir, debug_context: &FunctionDebugContext) +pub fn create_mir_scopes(ccx: &CrateContext, mir: &Mir, debug_context: &FunctionDebugContext) -> IndexVec { let null_scope = MirDebugScope { scope_metadata: ptr::null_mut(), @@ -71,7 +71,7 @@ pub fn create_mir_scopes(fcx: &FunctionContext, mir: &Mir, debug_context: &Funct // Instantiate all scopes. for idx in 0..mir.visibility_scopes.len() { let scope = VisibilityScope::new(idx); - make_mir_scope(fcx.ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes); + make_mir_scope(ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes); } scopes diff --git a/src/librustc_trans/debuginfo/doc.rs b/src/librustc_trans/debuginfo/doc.rs index bcf5eb992007..7a739071506d 100644 --- a/src/librustc_trans/debuginfo/doc.rs +++ b/src/librustc_trans/debuginfo/doc.rs @@ -45,7 +45,7 @@ //! //! All private state used by the module is stored within either the //! CrateDebugContext struct (owned by the CrateContext) or the -//! FunctionDebugContext (owned by the FunctionContext). +//! FunctionDebugContext (owned by the MirContext). //! //! This file consists of three conceptual sections: //! 1. The public interface of the module diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index f6428465eda2..1473e55b2643 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -1738,14 +1738,6 @@ pub fn create_global_var_metadata(cx: &CrateContext, let tcx = cx.tcx(); - // Don't create debuginfo for globals inlined from other crates. The other - // crate should already contain debuginfo for it. More importantly, the - // global might not even exist in un-inlined form anywhere which would lead - // to a linker errors. - if tcx.map.is_inlined_node_id(node_id) { - return; - } - let node_def_id = tcx.map.local_def_id(node_id); let (var_scope, span) = get_namespace_and_span_for_item(cx, node_def_id); diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 86099d241df6..9117f49cf3ea 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -27,7 +27,8 @@ use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use abi::Abi; -use common::{CrateContext, BlockAndBuilder}; +use common::CrateContext; +use builder::Builder; use monomorphize::{self, Instance}; use rustc::ty::{self, Ty}; use rustc::mir; @@ -423,7 +424,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } } -pub fn declare_local<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, +pub fn declare_local<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, dbg_context: &FunctionDebugContext, variable_name: ast::Name, variable_type: Ty<'tcx>, diff --git a/src/librustc_trans/debuginfo/source_loc.rs b/src/librustc_trans/debuginfo/source_loc.rs index e02c8be19a2f..e99e26261a3a 100644 --- a/src/librustc_trans/debuginfo/source_loc.rs +++ b/src/librustc_trans/debuginfo/source_loc.rs @@ -38,7 +38,7 @@ pub fn set_source_location( }; let dbg_loc = if function_debug_context.source_locations_enabled.get() { - debug!("set_source_location: {}", builder.ccx.sess().codemap().span_to_string(span)); + debug!("set_source_location: {}", builder.sess().codemap().span_to_string(span)); let loc = span_start(builder.ccx, span); InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) } else { diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 62141369caec..b24f00ee6976 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -13,6 +13,7 @@ // Code relating to drop glue. use std; +use std::ptr; use std::iter; use llvm; @@ -20,11 +21,14 @@ use llvm::{ValueRef, get_param}; use middle::lang_items::BoxFreeFnLangItem; use rustc::ty::subst::{Substs}; use rustc::traits; -use rustc::ty::{self, AdtKind, Ty, TypeFoldable}; +use rustc::ty::{self, layout, AdtDef, AdtKind, Ty, TypeFoldable}; use rustc::ty::subst::Kind; -use adt::{self, MaybeSizedValue}; +use rustc::mir::tcx::LvalueTy; +use mir::lvalue::LvalueRef; +use adt; use base::*; use callee::Callee; +use cleanup::CleanupScope; use common::*; use machine::*; use monomorphize; @@ -34,15 +38,12 @@ use type_of::{type_of, sizing_type_of, align_of}; use type_::Type; use value::Value; use Disr; -use cleanup::CleanupScope; +use builder::Builder; use syntax_pos::DUMMY_SP; -pub fn trans_exchange_free_ty<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, - ptr: MaybeSizedValue, - content_ty: Ty<'tcx> -) { +pub fn trans_exchange_free_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr: LvalueRef<'tcx>) { + let content_ty = ptr.ty.to_ty(bcx.tcx()); let def_id = langcall(bcx.tcx(), None, "", BoxFreeFnLangItem); let substs = bcx.tcx().mk_substs(iter::once(Kind::from(content_ty))); let callee = Callee::def(bcx.ccx, def_id, substs); @@ -50,7 +51,7 @@ pub fn trans_exchange_free_ty<'a, 'tcx>( let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); let llret = bcx.call(callee.reify(bcx.ccx), - &[ptr.value, ptr.meta][..1 + ptr.has_meta() as usize], None); + &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize], None); fn_ty.apply_attrs_callsite(llret); } @@ -79,7 +80,7 @@ pub fn get_drop_glue_type<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'t } match t.sty { ty::TyBox(typ) if !scx.type_needs_drop(typ) && scx.type_is_sized(typ) => { - scx.tcx().infer_ctxt(None, None, traits::Reveal::All).enter(|infcx| { + scx.tcx().infer_ctxt((), traits::Reveal::All).enter(|infcx| { let layout = t.layout(&infcx).unwrap(); if layout.size(&scx.tcx().data_layout).bytes() == 0 { // `Box` does not allocate. @@ -93,17 +94,17 @@ pub fn get_drop_glue_type<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'t } } -fn drop_ty<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, args: MaybeSizedValue, t: Ty<'tcx>) { - call_drop_glue(bcx, args, t, false, None) +fn drop_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, args: LvalueRef<'tcx>) { + call_drop_glue(bcx, args, false, None) } pub fn call_drop_glue<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, - mut args: MaybeSizedValue, - t: Ty<'tcx>, + bcx: &Builder<'a, 'tcx>, + mut args: LvalueRef<'tcx>, skip_dtor: bool, funclet: Option<&'a Funclet>, ) { + let t = args.ty.to_ty(bcx.tcx()); // NB: v is an *alias* of type t here, not a direct value. debug!("call_drop_glue(t={:?}, skip_dtor={})", t, skip_dtor); if bcx.ccx.shared().type_needs_drop(t) { @@ -116,11 +117,11 @@ pub fn call_drop_glue<'a, 'tcx>( let glue = get_drop_glue_core(ccx, g); let glue_type = get_drop_glue_type(ccx.shared(), t); if glue_type != t { - args.value = bcx.pointercast(args.value, type_of(ccx, glue_type).ptr_to()); + args.llval = bcx.pointercast(args.llval, type_of(ccx, glue_type).ptr_to()); } // No drop-hint ==> call standard drop glue - bcx.call(glue, &[args.value, args.meta][..1 + args.has_meta() as usize], + bcx.call(glue, &[args.llval, args.llextra][..1 + args.has_extra() as usize], funclet.map(|b| b.bundle())); } } @@ -173,8 +174,7 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi assert_eq!(g.ty(), get_drop_glue_type(ccx.shared(), g.ty())); let (llfn, _) = ccx.drop_glues().borrow().get(&g).unwrap().clone(); - let fcx = FunctionContext::new(ccx, llfn); - let mut bcx = fcx.get_entry_block(); + let mut bcx = Builder::new_block(ccx, llfn, "entry-block"); ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1); // All glue functions take values passed *by alias*; this is a @@ -194,9 +194,9 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi let value = get_param(llfn, 0); let ptr = if ccx.shared().type_is_sized(t) { - MaybeSizedValue::sized(value) + LvalueRef::new_sized_ty(value, t) } else { - MaybeSizedValue::unsized_(value, get_param(llfn, 1)) + LvalueRef::new_unsized_ty(value, get_param(llfn, 1), t) }; let skip_dtor = match g { @@ -211,14 +211,14 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi // a safe-guard, assert TyBox not used with TyContents. assert!(!skip_dtor); let ptr = if !bcx.ccx.shared().type_is_sized(content_ty) { - let llbox = bcx.load(get_dataptr(&bcx, ptr.value)); - let info = bcx.load(get_meta(&bcx, ptr.value)); - MaybeSizedValue::unsized_(llbox, info) + let llbox = bcx.load(get_dataptr(&bcx, ptr.llval)); + let info = bcx.load(get_meta(&bcx, ptr.llval)); + LvalueRef::new_unsized_ty(llbox, info, content_ty) } else { - MaybeSizedValue::sized(bcx.load(ptr.value)) + LvalueRef::new_sized_ty(bcx.load(ptr.llval), content_ty) }; - drop_ty(&bcx, ptr, content_ty); - trans_exchange_free_ty(&bcx, ptr, content_ty); + drop_ty(&bcx, ptr); + trans_exchange_free_ty(&bcx, ptr); bcx } ty::TyDynamic(..) => { @@ -226,8 +226,8 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi // versus without calling Drop::drop. Assert caller is // okay with always calling the Drop impl, if any. assert!(!skip_dtor); - let dtor = bcx.load(ptr.meta); - bcx.call(dtor, &[ptr.value], None); + let dtor = bcx.load(ptr.llextra); + bcx.call(dtor, &[ptr.llval], None); bcx } ty::TyAdt(def, ..) if def.dtor_kind().is_present() && !skip_dtor => { @@ -245,7 +245,7 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi // Issue #23611: schedule cleanup of contents, re-inspecting the // discriminant (if any) in case of variant swap in drop code. let contents_scope = if !shallow_drop { - bcx.fcx().schedule_drop_adt_contents(ptr, t) + CleanupScope::schedule_drop_adt_contents(&bcx, ptr) } else { CleanupScope::noop() }; @@ -262,9 +262,9 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi let callee = Callee::def(bcx.ccx, dtor_did, vtbl.substs); let fn_ty = callee.direct_fn_type(bcx.ccx, &[]); let llret; - let args = &[ptr.value, ptr.meta][..1 + ptr.has_meta() as usize]; + let args = &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize]; if let Some(landing_pad) = contents_scope.landing_pad { - let normal_bcx = bcx.fcx().build_new_block("normal-return"); + let normal_bcx = bcx.build_sibling_block("normal-return"); llret = bcx.invoke(callee.reify(ccx), args, normal_bcx.llbb(), landing_pad, None); bcx = normal_bcx; } else { @@ -279,7 +279,7 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi } _ => { if bcx.ccx.shared().type_needs_drop(t) { - drop_structural_ty(bcx, ptr, t) + drop_structural_ty(bcx, ptr) } else { bcx } @@ -288,8 +288,7 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi bcx.ret_void(); } -pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, - t: Ty<'tcx>, info: ValueRef) +pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, info: ValueRef) -> (ValueRef, ValueRef) { debug!("calculate size of DST: {}; with lost info: {:?}", t, Value(info)); @@ -397,60 +396,64 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, } // Iterates through the elements of a structural type, dropping them. -fn drop_structural_ty<'a, 'tcx>(cx: BlockAndBuilder<'a, 'tcx>, - ptr: MaybeSizedValue, - t: Ty<'tcx>) - -> BlockAndBuilder<'a, 'tcx> { - fn iter_variant<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>, - t: Ty<'tcx>, - av: adt::MaybeSizedValue, - variant: &'tcx ty::VariantDef, - substs: &Substs<'tcx>) { +fn drop_structural_ty<'a, 'tcx>( + cx: Builder<'a, 'tcx>, + mut ptr: LvalueRef<'tcx> +) -> Builder<'a, 'tcx> { + fn iter_variant_fields<'a, 'tcx>( + cx: &'a Builder<'a, 'tcx>, + av: LvalueRef<'tcx>, + adt_def: &'tcx AdtDef, + variant_index: usize, + substs: &'tcx Substs<'tcx> + ) { + let variant = &adt_def.variants[variant_index]; let tcx = cx.tcx(); for (i, field) in variant.fields.iter().enumerate() { let arg = monomorphize::field_ty(tcx, substs, field); - let field_ptr = adt::trans_field_ptr(&cx, t, av, Disr::from(variant.disr_val), i); - drop_ty(&cx, MaybeSizedValue::sized(field_ptr), arg); + let field_ptr = av.trans_field_ptr(&cx, i); + drop_ty(&cx, LvalueRef::new_sized_ty(field_ptr, arg)); } } let mut cx = cx; + let t = ptr.ty.to_ty(cx.tcx()); match t.sty { ty::TyClosure(def_id, substs) => { for (i, upvar_ty) in substs.upvar_tys(def_id, cx.tcx()).enumerate() { - let llupvar = adt::trans_field_ptr(&cx, t, ptr, Disr(0), i); - drop_ty(&cx, MaybeSizedValue::sized(llupvar), upvar_ty); + let llupvar = ptr.trans_field_ptr(&cx, i); + drop_ty(&cx, LvalueRef::new_sized_ty(llupvar, upvar_ty)); } } ty::TyArray(_, n) => { - let base = get_dataptr(&cx, ptr.value); + let base = get_dataptr(&cx, ptr.llval); let len = C_uint(cx.ccx, n); let unit_ty = t.sequence_element_type(cx.tcx()); cx = tvec::slice_for_each(&cx, base, unit_ty, len, - |bb, vv| drop_ty(bb, MaybeSizedValue::sized(vv), unit_ty)); + |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty))); } ty::TySlice(_) | ty::TyStr => { let unit_ty = t.sequence_element_type(cx.tcx()); - cx = tvec::slice_for_each(&cx, ptr.value, unit_ty, ptr.meta, - |bb, vv| drop_ty(bb, MaybeSizedValue::sized(vv), unit_ty)); + cx = tvec::slice_for_each(&cx, ptr.llval, unit_ty, ptr.llextra, + |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty))); } ty::TyTuple(ref args) => { for (i, arg) in args.iter().enumerate() { - let llfld_a = adt::trans_field_ptr(&cx, t, ptr, Disr(0), i); - drop_ty(&cx, MaybeSizedValue::sized(llfld_a), *arg); + let llfld_a = ptr.trans_field_ptr(&cx, i); + drop_ty(&cx, LvalueRef::new_sized_ty(llfld_a, *arg)); } } ty::TyAdt(adt, substs) => match adt.adt_kind() { AdtKind::Struct => { - let VariantInfo { fields, discr } = VariantInfo::from_ty(cx.tcx(), t, None); - for (i, &Field(_, field_ty)) in fields.iter().enumerate() { - let llfld_a = adt::trans_field_ptr(&cx, t, ptr, Disr::from(discr), i); - let ptr = if cx.ccx.shared().type_is_sized(field_ty) { - MaybeSizedValue::sized(llfld_a) - } else { - MaybeSizedValue::unsized_(llfld_a, ptr.meta) - }; - drop_ty(&cx, ptr, field_ty); + for (i, field) in adt.variants[0].fields.iter().enumerate() { + let field_ty = monomorphize::field_ty(cx.tcx(), substs, field); + let mut field_ptr = ptr.clone(); + field_ptr.llval = ptr.trans_field_ptr(&cx, i); + field_ptr.ty = LvalueTy::from_ty(field_ty); + if cx.ccx.shared().type_is_sized(field_ty) { + field_ptr.llextra = ptr::null_mut(); + } + drop_ty(&cx, field_ptr); } } AdtKind::Union => { @@ -462,16 +465,29 @@ fn drop_structural_ty<'a, 'tcx>(cx: BlockAndBuilder<'a, 'tcx>, // NB: we must hit the discriminant first so that structural // comparison know not to proceed when the discriminants differ. - match adt::trans_switch(&cx, t, ptr.value, false) { - (adt::BranchKind::Single, None) => { + // Obtain a representation of the discriminant sufficient to translate + // destructuring; this may or may not involve the actual discriminant. + let l = cx.ccx.layout_of(t); + match *l { + layout::Univariant { .. } | + layout::UntaggedUnion { .. } => { if n_variants != 0 { assert!(n_variants == 1); - iter_variant(&cx, t, ptr, &adt.variants[0], substs); + ptr.ty = LvalueTy::Downcast { + adt_def: adt, + substs: substs, + variant_index: 0, + }; + iter_variant_fields(&cx, ptr, &adt, 0, substs); } } - (adt::BranchKind::Switch, Some(lldiscrim_a)) => { + layout::CEnum { .. } | + layout::General { .. } | + layout::RawNullablePointer { .. } | + layout::StructWrappedNullablePointer { .. } => { + let lldiscrim_a = adt::trans_get_discr(&cx, t, ptr.llval, None, false); let tcx = cx.tcx(); - drop_ty(&cx, MaybeSizedValue::sized(lldiscrim_a), tcx.types.isize); + drop_ty(&cx, LvalueRef::new_sized_ty(lldiscrim_a, tcx.types.isize)); // Create a fall-through basic block for the "else" case of // the switch instruction we're about to generate. Note that @@ -486,23 +502,28 @@ fn drop_structural_ty<'a, 'tcx>(cx: BlockAndBuilder<'a, 'tcx>, // from the outer function, and any other use case will only // call this for an already-valid enum in which case the `ret // void` will never be hit. - let ret_void_cx = cx.fcx().build_new_block("enum-iter-ret-void"); + let ret_void_cx = cx.build_sibling_block("enum-iter-ret-void"); ret_void_cx.ret_void(); let llswitch = cx.switch(lldiscrim_a, ret_void_cx.llbb(), n_variants); - let next_cx = cx.fcx().build_new_block("enum-iter-next"); + let next_cx = cx.build_sibling_block("enum-iter-next"); - for variant in &adt.variants { + for (i, variant) in adt.variants.iter().enumerate() { let variant_cx_name = format!("enum-iter-variant-{}", &variant.disr_val.to_string()); - let variant_cx = cx.fcx().build_new_block(&variant_cx_name); + let variant_cx = cx.build_sibling_block(&variant_cx_name); let case_val = adt::trans_case(&cx, t, Disr::from(variant.disr_val)); variant_cx.add_case(llswitch, case_val, variant_cx.llbb()); - iter_variant(&variant_cx, t, ptr, variant, substs); + ptr.ty = LvalueTy::Downcast { + adt_def: adt, + substs: substs, + variant_index: i, + }; + iter_variant_fields(&variant_cx, ptr, &adt, i, substs); variant_cx.br(next_cx.llbb()); } cx = next_cx; } - _ => cx.ccx.sess().unimpl("value from adt::trans_switch in drop_structural_ty"), + _ => bug!("{} is not an enum.", t), } } }, diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index c4129b346e40..842a21e98db4 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -16,6 +16,7 @@ use llvm; use llvm::{ValueRef}; use abi::{Abi, FnType}; use adt; +use mir::lvalue::LvalueRef; use base::*; use common::*; use declare; @@ -24,10 +25,10 @@ use type_of; use machine; use type_::Type; use rustc::ty::{self, Ty}; -use Disr; use rustc::hir; use syntax::ast; use syntax::symbol::Symbol; +use builder::Builder; use rustc::session::Session; use syntax_pos::Span; @@ -87,14 +88,14 @@ fn get_simple_intrinsic(ccx: &CrateContext, name: &str) -> Option { /// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs, /// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics, /// add them to librustc_trans/trans/context.rs -pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, +pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, callee_ty: Ty<'tcx>, fn_ty: &FnType, llargs: &[ValueRef], llresult: ValueRef, span: Span) { let ccx = bcx.ccx; - let tcx = bcx.tcx(); + let tcx = ccx.tcx(); let (def_id, substs, fty) = match callee_ty.sty { ty::TyFnDef(def_id, substs, ref fty) => (def_id, substs, fty), @@ -125,7 +126,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, bcx.call(expect, &[llargs[0], C_bool(ccx, false)], None) } "try" => { - try_intrinsic(bcx, llargs[0], llargs[1], llargs[2], llresult); + try_intrinsic(bcx, ccx, llargs[0], llargs[1], llargs[2], llresult); C_nil(ccx) } "breakpoint" => { @@ -290,8 +291,8 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, let val = bcx.call(llfn, &[llargs[0], llargs[1]], None); let result = bcx.extract_value(val, 0); let overflow = bcx.zext(bcx.extract_value(val, 1), Type::bool(ccx)); - bcx.store(result, bcx.struct_gep(llresult, 0)); - bcx.store(overflow, bcx.struct_gep(llresult, 1)); + bcx.store(result, bcx.struct_gep(llresult, 0), None); + bcx.store(overflow, bcx.struct_gep(llresult, 1), None); C_nil(bcx.ccx) }, @@ -409,8 +410,8 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, failorder, weak); let result = bcx.extract_value(val, 0); let success = bcx.zext(bcx.extract_value(val, 1), Type::bool(bcx.ccx)); - bcx.store(result, bcx.struct_gep(llresult, 0)); - bcx.store(success, bcx.struct_gep(llresult, 1)); + bcx.store(result, bcx.struct_gep(llresult, 0), None); + bcx.store(success, bcx.struct_gep(llresult, 1), None); } else { invalid_monomorphization(sty); } @@ -533,7 +534,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, // qux` to be converted into `foo, bar, baz, qux`, integer // arguments to be truncated as needed and pointers to be // cast. - fn modify_as_needed<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, + fn modify_as_needed<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: &intrinsics::Type, arg_type: Ty<'tcx>, llarg: ValueRef) @@ -548,12 +549,8 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, // destructors, and the contents are SIMD // etc. assert!(!bcx.ccx.shared().type_needs_drop(arg_type)); - let arg = adt::MaybeSizedValue::sized(llarg); - (0..contents.len()) - .map(|i| { - bcx.load(adt::trans_field_ptr(bcx, arg_type, arg, Disr(0), i)) - }) - .collect() + let arg = LvalueRef::new_sized_ty(llarg, arg_type); + (0..contents.len()).map(|i| bcx.load(arg.trans_field_ptr(bcx, i))).collect() } intrinsics::Type::Pointer(_, Some(ref llvm_elem), _) => { let llvm_elem = one(ty_to_type(bcx.ccx, llvm_elem, &mut false)); @@ -615,7 +612,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, for i in 0..elems.len() { let val = bcx.extract_value(val, i); - bcx.store(val, bcx.struct_gep(llresult, i)); + bcx.store(val, bcx.struct_gep(llresult, i), None); } C_nil(ccx) } @@ -627,17 +624,14 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, if val_ty(llval) != Type::void(ccx) && machine::llsize_of_alloc(ccx, val_ty(llval)) != 0 { if let Some(ty) = fn_ty.ret.cast { let ptr = bcx.pointercast(llresult, ty.ptr_to()); - let store = bcx.store(llval, ptr); - unsafe { - llvm::LLVMSetAlignment(store, type_of::align_of(ccx, ret_ty)); - } + bcx.store(llval, ptr, Some(type_of::align_of(ccx, ret_ty))); } else { store_ty(bcx, llval, llresult, ret_ty); } } } -fn copy_intrinsic<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, +fn copy_intrinsic<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, allow_overlap: bool, volatile: bool, tp_ty: Ty<'tcx>, @@ -673,7 +667,7 @@ fn copy_intrinsic<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, } fn memset_intrinsic<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, volatile: bool, ty: Ty<'tcx>, dst: ValueRef, @@ -689,7 +683,8 @@ fn memset_intrinsic<'a, 'tcx>( } fn try_intrinsic<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, + ccx: &CrateContext, func: ValueRef, data: ValueRef, local_ptr: ValueRef, @@ -697,11 +692,11 @@ fn try_intrinsic<'a, 'tcx>( ) { if bcx.sess().no_landing_pads() { bcx.call(func, &[data], None); - bcx.store(C_null(Type::i8p(&bcx.ccx)), dest); + bcx.store(C_null(Type::i8p(&bcx.ccx)), dest, None); } else if wants_msvc_seh(bcx.sess()) { - trans_msvc_try(bcx, func, data, local_ptr, dest); + trans_msvc_try(bcx, ccx, func, data, local_ptr, dest); } else { - trans_gnu_try(bcx, func, data, local_ptr, dest); + trans_gnu_try(bcx, ccx, func, data, local_ptr, dest); } } @@ -712,24 +707,25 @@ fn try_intrinsic<'a, 'tcx>( // instructions are meant to work for all targets, as of the time of this // writing, however, LLVM does not recommend the usage of these new instructions // as the old ones are still more optimized. -fn trans_msvc_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, +fn trans_msvc_try<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, + ccx: &CrateContext, func: ValueRef, data: ValueRef, local_ptr: ValueRef, dest: ValueRef) { - let llfn = get_rust_try_fn(bcx.fcx(), &mut |bcx| { + let llfn = get_rust_try_fn(ccx, &mut |bcx| { let ccx = bcx.ccx; bcx.set_personality_fn(bcx.ccx.eh_personality()); - let normal = bcx.fcx().build_new_block("normal"); - let catchswitch = bcx.fcx().build_new_block("catchswitch"); - let catchpad = bcx.fcx().build_new_block("catchpad"); - let caught = bcx.fcx().build_new_block("caught"); + let normal = bcx.build_sibling_block("normal"); + let catchswitch = bcx.build_sibling_block("catchswitch"); + let catchpad = bcx.build_sibling_block("catchpad"); + let caught = bcx.build_sibling_block("caught"); - let func = llvm::get_param(bcx.fcx().llfn, 0); - let data = llvm::get_param(bcx.fcx().llfn, 1); - let local_ptr = llvm::get_param(bcx.fcx().llfn, 2); + let func = llvm::get_param(bcx.llfn(), 0); + let data = llvm::get_param(bcx.llfn(), 1); + let local_ptr = llvm::get_param(bcx.llfn(), 2); // We're generating an IR snippet that looks like: // @@ -771,7 +767,7 @@ fn trans_msvc_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, // // More information can be found in libstd's seh.rs implementation. let i64p = Type::i64(ccx).ptr_to(); - let slot = bcx.fcx().alloca(i64p, "slot"); + let slot = bcx.alloca(i64p, "slot"); bcx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None); @@ -791,8 +787,8 @@ fn trans_msvc_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, let val1 = C_i32(ccx, 1); let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1])); let local_ptr = catchpad.bitcast(local_ptr, i64p); - catchpad.store(arg1, local_ptr); - catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1])); + catchpad.store(arg1, local_ptr, None); + catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1]), None); catchpad.catch_ret(tok, caught.llbb()); caught.ret(C_i32(ccx, 1)); @@ -801,7 +797,7 @@ fn trans_msvc_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bcx.call(llfn, &[func, data, local_ptr], None); - bcx.store(ret, dest); + bcx.store(ret, dest, None); } // Definition of the standard "try" function for Rust using the GNU-like model @@ -815,12 +811,13 @@ fn trans_msvc_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, // function calling it, and that function may already have other personality // functions in play. By calling a shim we're guaranteed that our shim will have // the right personality function. -fn trans_gnu_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, +fn trans_gnu_try<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, + ccx: &CrateContext, func: ValueRef, data: ValueRef, local_ptr: ValueRef, dest: ValueRef) { - let llfn = get_rust_try_fn(bcx.fcx(), &mut |bcx| { + let llfn = get_rust_try_fn(ccx, &mut |bcx| { let ccx = bcx.ccx; // Translates the shims described above: @@ -840,12 +837,12 @@ fn trans_gnu_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, // expected to be `*mut *mut u8` for this to actually work, but that's // managed by the standard library. - let then = bcx.fcx().build_new_block("then"); - let catch = bcx.fcx().build_new_block("catch"); + let then = bcx.build_sibling_block("then"); + let catch = bcx.build_sibling_block("catch"); - let func = llvm::get_param(bcx.fcx().llfn, 0); - let data = llvm::get_param(bcx.fcx().llfn, 1); - let local_ptr = llvm::get_param(bcx.fcx().llfn, 2); + let func = llvm::get_param(bcx.llfn(), 0); + let data = llvm::get_param(bcx.llfn(), 1); + let local_ptr = llvm::get_param(bcx.llfn(), 2); bcx.invoke(func, &[data], then.llbb(), catch.llbb(), None); then.ret(C_i32(ccx, 0)); @@ -857,28 +854,27 @@ fn trans_gnu_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, // rust_try ignores the selector. let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); - let vals = catch.landing_pad(lpad_ty, bcx.ccx.eh_personality(), 1, catch.fcx().llfn); + let vals = catch.landing_pad(lpad_ty, bcx.ccx.eh_personality(), 1, catch.llfn()); catch.add_clause(vals, C_null(Type::i8p(ccx))); let ptr = catch.extract_value(vals, 0); - catch.store(ptr, catch.bitcast(local_ptr, Type::i8p(ccx).ptr_to())); + catch.store(ptr, catch.bitcast(local_ptr, Type::i8p(ccx).ptr_to()), None); catch.ret(C_i32(ccx, 1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bcx.call(llfn, &[func, data, local_ptr], None); - bcx.store(ret, dest); + bcx.store(ret, dest, None); } // Helper function to give a Block to a closure to translate a shim function. // This is currently primarily used for the `try` intrinsic functions above. -fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, +fn gen_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, inputs: Vec>, output: Ty<'tcx>, - trans: &mut for<'b> FnMut(BlockAndBuilder<'b, 'tcx>)) + trans: &mut for<'b> FnMut(Builder<'b, 'tcx>)) -> ValueRef { - let ccx = fcx.ccx; let sig = ccx.tcx().mk_fn_sig(inputs.into_iter(), output, false); let rust_fn_ty = ccx.tcx().mk_fn_ptr(ccx.tcx().mk_bare_fn(ty::BareFnTy { @@ -887,8 +883,8 @@ fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, sig: ty::Binder(sig) })); let llfn = declare::define_internal_fn(ccx, name, rust_fn_ty); - let fcx = FunctionContext::new(ccx, llfn); - trans(fcx.get_entry_block()); + let bcx = Builder::new_block(ccx, llfn, "entry-block"); + trans(bcx); llfn } @@ -896,10 +892,9 @@ fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, // catch exceptions. // // This function is only generated once and is then cached. -fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, - trans: &mut for<'b> FnMut(BlockAndBuilder<'b, 'tcx>)) +fn get_rust_try_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + trans: &mut for<'b> FnMut(Builder<'b, 'tcx>)) -> ValueRef { - let ccx = fcx.ccx; if let Some(llfn) = ccx.rust_try_fn().get() { return llfn; } @@ -913,7 +908,7 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, sig: ty::Binder(tcx.mk_fn_sig(iter::once(i8p), tcx.mk_nil(), false)), })); let output = tcx.types.i32; - let rust_try = gen_fn(fcx, "__rust_try", vec![fn_ty, i8p, i8p], output, trans); + let rust_try = gen_fn(ccx, "__rust_try", vec![fn_ty, i8p, i8p], output, trans); ccx.rust_try_fn().set(Some(rust_try)); return rust_try } @@ -923,7 +918,7 @@ fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { } fn generic_simd_intrinsic<'a, 'tcx>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, name: &str, callee_ty: Ty<'tcx>, llargs: &[ValueRef], diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 3a4171e1d0ec..d8c0bde963e3 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -21,7 +21,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![feature(associated_consts)] #![feature(box_patterns)] diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index cf50e7be2afb..aecba2f57e52 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -13,6 +13,7 @@ use llvm::{ValueRef, get_params}; use rustc::traits; use callee::{Callee, CalleeData}; use common::*; +use builder::Builder; use consts; use declare; use glue; @@ -27,7 +28,7 @@ use rustc::ty; const VTABLE_OFFSET: usize = 3; /// Extracts a method from a trait object's vtable, at the specified index. -pub fn get_virtual_method<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, +pub fn get_virtual_method<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, llvtable: ValueRef, vtable_index: usize) -> ValueRef { @@ -75,10 +76,9 @@ pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, let llfn = declare::define_internal_fn(ccx, &function_name, callee.ty); attributes::set_frame_pointer_elimination(ccx, llfn); - let fcx = FunctionContext::new(ccx, llfn); - let bcx = fcx.get_entry_block(); + let bcx = Builder::new_block(ccx, llfn, "entry-block"); - let mut llargs = get_params(fcx.llfn); + let mut llargs = get_params(llfn); let fn_ret = callee.ty.fn_ret(); let fn_ty = callee.direct_fn_type(ccx, &[]); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index ecedcd68382d..7d4a1ab5ae70 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -14,16 +14,17 @@ use rustc::middle::lang_items; use rustc::ty::{self, layout}; use rustc::mir; use abi::{Abi, FnType, ArgType}; -use adt::{self, MaybeSizedValue}; +use adt; use base::{self, Lifetime}; use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual}; -use common::{self, BlockAndBuilder, Funclet}; +use builder::Builder; +use common::{self, Funclet}; use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; use consts; use Disr; use machine::{llalign_of_min, llbitsize_of_real}; use meth; -use type_of; +use type_of::{self, align_of}; use glue; use type_::Type; @@ -31,17 +32,19 @@ use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashMap; use syntax::symbol::Symbol; +use std::cmp; + use super::{MirContext, LocalRef}; use super::analyze::CleanupKind; use super::constant::Const; -use super::lvalue::{LvalueRef}; +use super::lvalue::LvalueRef; use super::operand::OperandRef; use super::operand::OperandValue::{Pair, Ref, Immediate}; impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_block(&mut self, bb: mir::BasicBlock, funclets: &IndexVec>) { - let mut bcx = self.build_block(bb); + let mut bcx = self.get_builder(bb); let data = &self.mir[bb]; debug!("trans_block({:?}={:?})", bb, data); @@ -55,7 +58,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let cleanup_pad = funclet.map(|lp| lp.cleanuppad()); let cleanup_bundle = funclet.map(|l| l.bundle()); - let funclet_br = |this: &Self, bcx: BlockAndBuilder, bb: mir::BasicBlock| { + let funclet_br = |this: &Self, bcx: Builder, bb: mir::BasicBlock| { let lltarget = this.blocks[bb]; if let Some(cp) = cleanup_pad { match this.cleanup_kinds[bb] { @@ -82,7 +85,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { debug!("llblock: creating cleanup trampoline for {:?}", target); let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); - let trampoline = this.fcx.build_new_block(name); + let trampoline = this.new_block(name); trampoline.cleanup_ret(cp, Some(lltarget)); trampoline.llbb() } @@ -206,8 +209,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; let llslot = match op.val { Immediate(_) | Pair(..) => { - let llscratch = bcx.fcx().alloca(ret.original_ty, "ret"); - self.store_operand(&bcx, llscratch, op); + let llscratch = bcx.alloca(ret.original_ty, "ret"); + self.store_operand(&bcx, llscratch, op, None); llscratch } Ref(llval) => llval @@ -220,7 +223,11 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { load } else { let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER)); - op.pack_if_pair(&bcx).immediate() + if let Ref(llval) = op.val { + base::load_ty(&bcx, llval, op.ty) + } else { + op.pack_if_pair(&bcx).immediate() + } }; bcx.ret(llval); } @@ -239,20 +246,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return; } - let lvalue = self.trans_lvalue(&bcx, location); + let mut lvalue = self.trans_lvalue(&bcx, location); let drop_fn = glue::get_drop_glue(bcx.ccx, ty); let drop_ty = glue::get_drop_glue_type(bcx.ccx.shared(), ty); - let ptr = if bcx.ccx.shared().type_is_sized(ty) { - let value = if drop_ty != ty { - bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx, drop_ty).ptr_to()) - } else { - lvalue.llval - }; - MaybeSizedValue::sized(value) - } else { - MaybeSizedValue::unsized_(lvalue.llval, lvalue.llextra) - }; - let args = &[ptr.value, ptr.meta][..1 + ptr.has_meta() as usize]; + if bcx.ccx.shared().type_is_sized(ty) && drop_ty != ty { + lvalue.llval = bcx.pointercast( + lvalue.llval, type_of::type_of(bcx.ccx, drop_ty).ptr_to()); + } + let args = &[lvalue.llval, lvalue.llextra][..1 + lvalue.has_extra() as usize]; if let Some(unwind) = unwind { bcx.invoke( drop_fn, @@ -299,7 +300,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Create the failure block and the conditional branch to it. let lltarget = llblock(self, target); - let panic_block = self.fcx.build_new_block("panic"); + let panic_block = self.new_block("panic"); if expected { bcx.cond_br(cond, lltarget, panic_block.llbb()); } else { @@ -424,7 +425,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // The first argument is a thin destination pointer. let llptr = self.trans_operand(&bcx, &args[0]).immediate(); let val = self.trans_operand(&bcx, &args[1]); - self.store_operand(&bcx, llptr, val); + self.store_operand(&bcx, llptr, val, None); funclet_br(self, bcx, target); return; } @@ -582,15 +583,13 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { fn_ty.apply_attrs_callsite(invokeret); if let Some((_, target)) = *destination { - let ret_bcx = self.build_block(target); - ret_bcx.at_start(|ret_bcx| { - self.set_debug_loc(&ret_bcx, terminator.source_info); - let op = OperandRef { - val: Immediate(invokeret), - ty: sig.output(), - }; - self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op); - }); + let ret_bcx = self.get_builder(target); + self.set_debug_loc(&ret_bcx, terminator.source_info); + let op = OperandRef { + val: Immediate(invokeret), + ty: sig.output(), + }; + self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op); } } else { let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle); @@ -611,7 +610,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } fn trans_argument(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, op: OperandRef<'tcx>, llargs: &mut Vec, fn_ty: &FnType, @@ -656,8 +655,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let (mut llval, by_ref) = match op.val { Immediate(_) | Pair(..) => { if arg.is_indirect() || arg.cast.is_some() { - let llscratch = bcx.fcx().alloca(arg.original_ty, "arg"); - self.store_operand(bcx, llscratch, op); + let llscratch = bcx.alloca(arg.original_ty, "arg"); + self.store_operand(bcx, llscratch, op, None); (llscratch, true) } else { (op.pack_if_pair(bcx).immediate(), false) @@ -687,7 +686,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } fn trans_arguments_untupled(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, operand: &mir::Operand<'tcx>, llargs: &mut Vec, fn_ty: &FnType, @@ -704,9 +703,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Handle both by-ref and immediate tuples. match tuple.val { Ref(llval) => { - let base = adt::MaybeSizedValue::sized(llval); for (n, &ty) in arg_types.iter().enumerate() { - let ptr = adt::trans_field_ptr(bcx, tuple.ty, base, Disr(0), n); + let ptr = LvalueRef::new_sized_ty(llval, tuple.ty); + let ptr = ptr.trans_field_ptr(bcx, n); let val = if common::type_is_fat_ptr(bcx.ccx, ty) { let (lldata, llextra) = base::load_fat_ptr(bcx, ptr, ty); Pair(lldata, llextra) @@ -763,13 +762,13 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } - fn get_personality_slot(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>) -> ValueRef { + fn get_personality_slot(&mut self, bcx: &Builder<'a, 'tcx>) -> ValueRef { let ccx = bcx.ccx; if let Some(slot) = self.llpersonalityslot { slot } else { let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); - let slot = bcx.fcx().alloca(llretty, "personalityslot"); + let slot = bcx.alloca(llretty, "personalityslot"); self.llpersonalityslot = Some(slot); Lifetime::Start.call(bcx, slot); slot @@ -788,36 +787,42 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return self.blocks[target_bb]; } - let target = self.build_block(target_bb); + let target = self.get_builder(target_bb); - let bcx = self.fcx.build_new_block("cleanup"); + let bcx = self.new_block("cleanup"); self.landing_pads[target_bb] = Some(bcx.llbb()); let ccx = bcx.ccx; let llpersonality = self.ccx.eh_personality(); let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); - let llretval = bcx.landing_pad(llretty, llpersonality, 1, self.fcx.llfn); + let llretval = bcx.landing_pad(llretty, llpersonality, 1, self.llfn); bcx.set_cleanup(llretval); let slot = self.get_personality_slot(&bcx); - bcx.store(llretval, slot); + bcx.store(llretval, slot, None); bcx.br(target.llbb()); bcx.llbb() } fn unreachable_block(&mut self) -> BasicBlockRef { self.unreachable_block.unwrap_or_else(|| { - let bl = self.fcx.build_new_block("unreachable"); + let bl = self.new_block("unreachable"); bl.unreachable(); self.unreachable_block = Some(bl.llbb()); bl.llbb() }) } - pub fn build_block(&self, bb: mir::BasicBlock) -> BlockAndBuilder<'a, 'tcx> { - BlockAndBuilder::new(self.blocks[bb], self.fcx) + pub fn new_block(&self, name: &str) -> Builder<'a, 'tcx> { + Builder::new_block(self.ccx, self.llfn, name) } - fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>, + pub fn get_builder(&self, bb: mir::BasicBlock) -> Builder<'a, 'tcx> { + let builder = Builder::with_ccx(self.ccx); + builder.position_at_end(self.blocks[bb]); + builder + } + + fn make_return_dest(&mut self, bcx: &Builder<'a, 'tcx>, dest: &mir::Lvalue<'tcx>, fn_ret_ty: &ArgType, llargs: &mut Vec, is_intrinsic: bool) -> ReturnDest { // If the return is ignored, we can just return a do-nothing ReturnDest @@ -834,14 +839,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { return if fn_ret_ty.is_indirect() { // Odd, but possible, case, we have an operand temporary, // but the calling convention has an indirect return. - let tmp = base::alloc_ty(bcx, ret_ty, "tmp_ret"); + let tmp = bcx.alloca_ty(ret_ty, "tmp_ret"); llargs.push(tmp); ReturnDest::IndirectOperand(tmp, index) } else if is_intrinsic { // Currently, intrinsics always need a location to store // the result. so we create a temporary alloca for the // result - let tmp = base::alloc_ty(bcx, ret_ty, "tmp_ret"); + let tmp = bcx.alloca_ty(ret_ty, "tmp_ret"); ReturnDest::IndirectOperand(tmp, index) } else { ReturnDest::DirectOperand(index) @@ -862,7 +867,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } } - fn trans_transmute(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>, + fn trans_transmute(&mut self, bcx: &Builder<'a, 'tcx>, src: &mir::Operand<'tcx>, dst: LvalueRef<'tcx>) { let mut val = self.trans_operand(bcx, src); if let ty::TyFnDef(def_id, substs, _) = val.ty.sty { @@ -884,13 +889,16 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let llty = type_of::type_of(bcx.ccx, val.ty); let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to()); - self.store_operand(bcx, cast_ptr, val); + let in_type = val.ty; + let out_type = dst.ty.to_ty(bcx.tcx());; + let llalign = cmp::min(align_of(bcx.ccx, in_type), align_of(bcx.ccx, out_type)); + self.store_operand(bcx, cast_ptr, val, Some(llalign)); } // Stores the return value of a function call into it's final location. fn store_return(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, dest: ReturnDest, ret_ty: ArgType, op: OperandRef<'tcx>) { @@ -906,7 +914,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { DirectOperand(index) => { // If there is a cast, we have to store and reload. let op = if ret_ty.cast.is_some() { - let tmp = base::alloc_ty(bcx, op.ty, "tmp_ret"); + let tmp = bcx.alloca_ty(op.ty, "tmp_ret"); ret_ty.store(bcx, op.immediate(), tmp); self.trans_load(bcx, tmp, op.ty) } else { diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 700894c255da..9ac2bea3b82f 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -18,16 +18,17 @@ use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; use rustc::mir; use rustc::mir::tcx::LvalueTy; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, layout, Ty, TyCtxt, TypeFoldable}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::subst::Substs; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use {abi, adt, base, Disr, machine}; use callee::Callee; -use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty}; +use builder::Builder; +use common::{self, CrateContext, const_get_elt, val_ty}; use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral, C_big_integral}; -use common::{C_null, C_struct, C_str_slice, C_undef, C_uint}; -use common::{const_to_opt_u128}; +use common::{C_null, C_struct, C_str_slice, C_undef, C_uint, C_vector, is_undef}; +use common::const_to_opt_u128; use consts; use monomorphize::{self, Instance}; use type_of; @@ -100,7 +101,6 @@ impl<'tcx> Const<'tcx> { bug!("MIR must not use `{:?}` (which refers to a local ID)", cv) } ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false), - ConstVal::Dummy => bug!(), }; assert!(!ty.has_erasable_regions()); @@ -548,16 +548,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::AggregateKind::Adt(..) | mir::AggregateKind::Closure(..) | mir::AggregateKind::Tuple => { - let disr = match *kind { - mir::AggregateKind::Adt(adt_def, index, _, _) => { - Disr::from(adt_def.variants[index].disr_val) - } - _ => Disr(0) - }; - Const::new( - adt::trans_const(self.ccx, dest_ty, disr, &fields), - dest_ty - ) + Const::new(trans_const(self.ccx, dest_ty, kind, &fields), dest_ty) } } } @@ -900,7 +891,7 @@ pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_constant(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, constant: &mir::Constant<'tcx>) -> Const<'tcx> { @@ -945,3 +936,159 @@ pub fn trans_static_initializer(ccx: &CrateContext, def_id: DefId) let instance = Instance::mono(ccx.shared(), def_id); MirConstContext::trans_def(ccx, instance, IndexVec::new()).map(|c| c.llval) } + +/// Construct a constant value, suitable for initializing a +/// GlobalVariable, given a case and constant values for its fields. +/// Note that this may have a different LLVM type (and different +/// alignment!) from the representation's `type_of`, so it needs a +/// pointer cast before use. +/// +/// The LLVM type system does not directly support unions, and only +/// pointers can be bitcast, so a constant (and, by extension, the +/// GlobalVariable initialized by it) will have a type that can vary +/// depending on which case of an enum it is. +/// +/// To understand the alignment situation, consider `enum E { V64(u64), +/// V32(u32, u32) }` on Windows. The type has 8-byte alignment to +/// accommodate the u64, but `V32(x, y)` would have LLVM type `{i32, +/// i32, i32}`, which is 4-byte aligned. +/// +/// Currently the returned value has the same size as the type, but +/// this could be changed in the future to avoid allocating unnecessary +/// space after values of shorter-than-maximum cases. +fn trans_const<'a, 'tcx>( + ccx: &CrateContext<'a, 'tcx>, + t: Ty<'tcx>, + kind: &mir::AggregateKind, + vals: &[ValueRef] +) -> ValueRef { + let l = ccx.layout_of(t); + let dl = &ccx.tcx().data_layout; + let variant_index = match *kind { + mir::AggregateKind::Adt(_, index, _, _) => index, + _ => 0, + }; + match *l { + layout::CEnum { discr: d, min, max, .. } => { + let discr = match *kind { + mir::AggregateKind::Adt(adt_def, _, _, _) => { + Disr::from(adt_def.variants[variant_index].disr_val) + }, + _ => Disr(0), + }; + assert_eq!(vals.len(), 0); + adt::assert_discr_in_range(Disr(min), Disr(max), discr); + C_integral(Type::from_integer(ccx, d), discr.0, true) + } + layout::General { discr: d, ref variants, .. } => { + let variant = &variants[variant_index]; + let lldiscr = C_integral(Type::from_integer(ccx, d), variant_index as u64, true); + let mut vals_with_discr = vec![lldiscr]; + vals_with_discr.extend_from_slice(vals); + let mut contents = build_const_struct(ccx, &variant, &vals_with_discr[..]); + let needed_padding = l.size(dl).bytes() - variant.stride().bytes(); + if needed_padding > 0 { + contents.push(padding(ccx, needed_padding)); + } + C_struct(ccx, &contents[..], false) + } + layout::UntaggedUnion { ref variants, .. }=> { + assert_eq!(variant_index, 0); + let contents = build_const_union(ccx, variants, vals[0]); + C_struct(ccx, &contents, variants.packed) + } + layout::Univariant { ref variant, .. } => { + assert_eq!(variant_index, 0); + let contents = build_const_struct(ccx, &variant, vals); + C_struct(ccx, &contents[..], variant.packed) + } + layout::Vector { .. } => { + C_vector(vals) + } + layout::RawNullablePointer { nndiscr, .. } => { + let nnty = adt::compute_fields(ccx, t, nndiscr as usize, false)[0]; + if variant_index as u64 == nndiscr { + assert_eq!(vals.len(), 1); + vals[0] + } else { + C_null(type_of::sizing_type_of(ccx, nnty)) + } + } + layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { + if variant_index as u64 == nndiscr { + C_struct(ccx, &build_const_struct(ccx, &nonnull, vals), false) + } else { + let fields = adt::compute_fields(ccx, t, nndiscr as usize, false); + let vals = fields.iter().map(|&ty| { + // Always use null even if it's not the `discrfield`th + // field; see #8506. + C_null(type_of::sizing_type_of(ccx, ty)) + }).collect::>(); + C_struct(ccx, &build_const_struct(ccx, &nonnull, &vals[..]), false) + } + } + _ => bug!("trans_const: cannot handle type {} repreented as {:#?}", t, l) + } +} + +/// Building structs is a little complicated, because we might need to +/// insert padding if a field's value is less aligned than its type. +/// +/// Continuing the example from `trans_const`, a value of type `(u32, +/// E)` should have the `E` at offset 8, but if that field's +/// initializer is 4-byte aligned then simply translating the tuple as +/// a two-element struct will locate it at offset 4, and accesses to it +/// will read the wrong memory. +fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + st: &layout::Struct, + vals: &[ValueRef]) + -> Vec { + assert_eq!(vals.len(), st.offsets.len()); + + if vals.len() == 0 { + return Vec::new(); + } + + // offset of current value + let mut offset = 0; + let mut cfields = Vec::new(); + cfields.reserve(st.offsets.len()*2); + + let parts = st.field_index_by_increasing_offset().map(|i| { + (&vals[i], st.offsets[i].bytes()) + }); + for (&val, target_offset) in parts { + if offset < target_offset { + cfields.push(padding(ccx, target_offset - offset)); + offset = target_offset; + } + assert!(!is_undef(val)); + cfields.push(val); + offset += machine::llsize_of_alloc(ccx, val_ty(val)); + } + + if offset < st.stride().bytes() { + cfields.push(padding(ccx, st.stride().bytes() - offset)); + } + + cfields +} + +fn build_const_union<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + un: &layout::Union, + field_val: ValueRef) + -> Vec { + let mut cfields = vec![field_val]; + + let offset = machine::llsize_of_alloc(ccx, val_ty(field_val)); + let size = un.stride().bytes(); + if offset != size { + cfields.push(padding(ccx, size - offset)); + } + + cfields +} + +fn padding(ccx: &CrateContext, size: u64) -> ValueRef { + C_undef(Type::array(&Type::i8(ccx), size)) +} diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index 0cd7f007c5df..bd6e70639bba 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -9,18 +9,20 @@ // except according to those terms. use llvm::ValueRef; -use rustc::ty::{self, Ty, TypeFoldable}; +use rustc::ty::{self, layout, Ty, TypeFoldable}; use rustc::mir; use rustc::mir::tcx::LvalueTy; use rustc_data_structures::indexed_vec::Idx; use adt; -use base; -use common::{self, BlockAndBuilder, CrateContext, C_uint, C_undef}; +use builder::Builder; +use common::{self, CrateContext, C_uint, C_undef}; use consts; use machine; use type_of::type_of; use type_of; -use Disr; +use type_::Type; +use value::Value; +use glue; use std::ptr; @@ -39,22 +41,24 @@ pub struct LvalueRef<'tcx> { pub ty: LvalueTy<'tcx>, } -impl<'tcx> LvalueRef<'tcx> { +impl<'a, 'tcx> LvalueRef<'tcx> { pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> { LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty } } - pub fn alloca<'a>(bcx: &BlockAndBuilder<'a, 'tcx>, - ty: Ty<'tcx>, - name: &str) - -> LvalueRef<'tcx> - { - assert!(!ty.has_erasable_regions()); - let lltemp = base::alloc_ty(bcx, ty, name); - LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty)) + pub fn new_sized_ty(llval: ValueRef, ty: Ty<'tcx>) -> LvalueRef<'tcx> { + LvalueRef::new_sized(llval, LvalueTy::from_ty(ty)) } - pub fn len<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef { + pub fn new_unsized_ty(llval: ValueRef, llextra: ValueRef, ty: Ty<'tcx>) -> LvalueRef<'tcx> { + LvalueRef { + llval: llval, + llextra: llextra, + ty: LvalueTy::from_ty(ty), + } + } + + pub fn len(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef { let ty = self.ty.to_ty(ccx.tcx()); match ty.sty { ty::TyArray(_, n) => common::C_uint(ccx, n), @@ -65,17 +69,170 @@ impl<'tcx> LvalueRef<'tcx> { _ => bug!("unexpected type `{}` in LvalueRef::len", ty) } } + + pub fn has_extra(&self) -> bool { + !self.llextra.is_null() + } + + fn struct_field_ptr( + self, + bcx: &Builder<'a, 'tcx>, + st: &layout::Struct, + fields: &Vec>, + ix: usize, + needs_cast: bool + ) -> ValueRef { + let fty = fields[ix]; + let ccx = bcx.ccx; + + let ptr_val = if needs_cast { + let fields = st.field_index_by_increasing_offset().map(|i| { + type_of::in_memory_type_of(ccx, fields[i]) + }).collect::>(); + let real_ty = Type::struct_(ccx, &fields[..], st.packed); + bcx.pointercast(self.llval, real_ty.ptr_to()) + } else { + self.llval + }; + + // Simple case - we can just GEP the field + // * First field - Always aligned properly + // * Packed struct - There is no alignment padding + // * Field is sized - pointer is properly aligned already + if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed || + bcx.ccx.shared().type_is_sized(fty) { + return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize); + } + + // If the type of the last field is [T] or str, then we don't need to do + // any adjusments + match fty.sty { + ty::TySlice(..) | ty::TyStr => { + return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize); + } + _ => () + } + + // There's no metadata available, log the case and just do the GEP. + if !self.has_extra() { + debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment", + ix, Value(ptr_val)); + return bcx.struct_gep(ptr_val, ix); + } + + // We need to get the pointer manually now. + // We do this by casting to a *i8, then offsetting it by the appropriate amount. + // We do this instead of, say, simply adjusting the pointer from the result of a GEP + // because the field may have an arbitrary alignment in the LLVM representation + // anyway. + // + // To demonstrate: + // struct Foo { + // x: u16, + // y: T + // } + // + // The type Foo> is represented in LLVM as { u16, { u16, u8 }}, meaning that + // the `y` field has 16-bit alignment. + + let meta = self.llextra; + + + let offset = st.offsets[ix].bytes(); + let unaligned_offset = C_uint(bcx.ccx, offset); + + // Get the alignment of the field + let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta); + + // Bump the unaligned offset up to the appropriate alignment using the + // following expression: + // + // (unaligned offset + (align - 1)) & -align + + // Calculate offset + let align_sub_1 = bcx.sub(align, C_uint(bcx.ccx, 1u64)); + let offset = bcx.and(bcx.add(unaligned_offset, align_sub_1), + bcx.neg(align)); + + debug!("struct_field_ptr: DST field offset: {:?}", Value(offset)); + + // Cast and adjust pointer + let byte_ptr = bcx.pointercast(ptr_val, Type::i8p(bcx.ccx)); + let byte_ptr = bcx.gep(byte_ptr, &[offset]); + + // Finally, cast back to the type expected + let ll_fty = type_of::in_memory_type_of(bcx.ccx, fty); + debug!("struct_field_ptr: Field type is {:?}", ll_fty); + bcx.pointercast(byte_ptr, ll_fty.ptr_to()) + } + + /// Access a field, at a point when the value's case is known. + pub fn trans_field_ptr(self, bcx: &Builder<'a, 'tcx>, ix: usize) -> ValueRef { + let discr = match self.ty { + LvalueTy::Ty { .. } => 0, + LvalueTy::Downcast { variant_index, .. } => variant_index, + }; + let t = self.ty.to_ty(bcx.tcx()); + let l = bcx.ccx.layout_of(t); + // Note: if this ever needs to generate conditionals (e.g., if we + // decide to do some kind of cdr-coding-like non-unique repr + // someday), it will need to return a possibly-new bcx as well. + match *l { + layout::Univariant { ref variant, .. } => { + assert_eq!(discr, 0); + self.struct_field_ptr(bcx, &variant, + &adt::compute_fields(bcx.ccx, t, 0, false), ix, false) + } + layout::Vector { count, .. } => { + assert_eq!(discr, 0); + assert!((ix as u64) < count); + bcx.struct_gep(self.llval, ix) + } + layout::General { discr: d, ref variants, .. } => { + let mut fields = adt::compute_fields(bcx.ccx, t, discr, false); + fields.insert(0, d.to_ty(&bcx.tcx(), false)); + self.struct_field_ptr(bcx, &variants[discr], &fields, ix + 1, true) + } + layout::UntaggedUnion { .. } => { + let fields = adt::compute_fields(bcx.ccx, t, 0, false); + let ty = type_of::in_memory_type_of(bcx.ccx, fields[ix]); + bcx.pointercast(self.llval, ty.ptr_to()) + } + layout::RawNullablePointer { nndiscr, .. } | + layout::StructWrappedNullablePointer { nndiscr, .. } if discr as u64 != nndiscr => { + let nullfields = adt::compute_fields(bcx.ccx, t, (1-nndiscr) as usize, false); + // The unit-like case might have a nonzero number of unit-like fields. + // (e.d., Result of Either with (), as one side.) + let ty = type_of::type_of(bcx.ccx, nullfields[ix]); + assert_eq!(machine::llsize_of_alloc(bcx.ccx, ty), 0); + bcx.pointercast(self.llval, ty.ptr_to()) + } + layout::RawNullablePointer { nndiscr, .. } => { + let nnty = adt::compute_fields(bcx.ccx, t, nndiscr as usize, false)[0]; + assert_eq!(ix, 0); + assert_eq!(discr as u64, nndiscr); + let ty = type_of::type_of(bcx.ccx, nnty); + bcx.pointercast(self.llval, ty.ptr_to()) + } + layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { + assert_eq!(discr as u64, nndiscr); + self.struct_field_ptr(bcx, &nonnull, + &adt::compute_fields(bcx.ccx, t, discr, false), ix, false) + } + _ => bug!("element access in type without elements: {} represented as {:#?}", t, l) + } + } } impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_lvalue(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, lvalue: &mir::Lvalue<'tcx>) -> LvalueRef<'tcx> { debug!("trans_lvalue(lvalue={:?})", lvalue); let ccx = bcx.ccx; - let tcx = bcx.tcx(); + let tcx = ccx.tcx(); if let mir::Lvalue::Local(index) = *lvalue { match self.locals[index] { @@ -134,26 +291,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let (llprojected, llextra) = match projection.elem { mir::ProjectionElem::Deref => bug!(), mir::ProjectionElem::Field(ref field, _) => { - let base_ty = tr_base.ty.to_ty(tcx); - let discr = match tr_base.ty { - LvalueTy::Ty { .. } => 0, - LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v, - }; - let discr = discr as u64; - let is_sized = self.ccx.shared().type_is_sized(projected_ty.to_ty(tcx)); - let base = if is_sized { - adt::MaybeSizedValue::sized(tr_base.llval) - } else { - adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra) - }; - let llprojected = adt::trans_field_ptr(bcx, base_ty, base, Disr(discr), - field.index()); - let llextra = if is_sized { + let llextra = if self.ccx.shared().type_is_sized(projected_ty.to_ty(tcx)) { ptr::null_mut() } else { tr_base.llextra }; - (llprojected, llextra) + (tr_base.trans_field_ptr(bcx, field.index()), llextra) } mir::ProjectionElem::Index(ref index) => { let index = self.trans_operand(bcx, index); @@ -214,7 +357,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Perform an action using the given Lvalue. // If the Lvalue is an empty LocalRef::Operand, then a temporary stack slot // is created first, then used as an operand to update the Lvalue. - pub fn with_lvalue_ref(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>, + pub fn with_lvalue_ref(&mut self, bcx: &Builder<'a, 'tcx>, lvalue: &mir::Lvalue<'tcx>, f: F) -> U where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U { @@ -223,9 +366,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { LocalRef::Lvalue(lvalue) => f(self, lvalue), LocalRef::Operand(None) => { let lvalue_ty = self.monomorphized_lvalue_ty(lvalue); - let lvalue = LvalueRef::alloca(bcx, - lvalue_ty, - "lvalue_temp"); + assert!(!lvalue_ty.has_erasable_regions()); + let lltemp = bcx.alloca_ty(lvalue_ty, "lvalue_temp"); + let lvalue = LvalueRef::new_sized(lltemp, LvalueTy::from_ty(lvalue_ty)); let ret = f(self, lvalue); let op = self.trans_load(bcx, lvalue.llval, lvalue_ty); self.locals[index] = LocalRef::Operand(Some(op)); @@ -254,18 +397,13 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { /// than we are. /// /// nmatsakis: is this still necessary? Not sure. - fn prepare_index(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, - llindex: ValueRef) - -> ValueRef - { - let ccx = bcx.ccx; + fn prepare_index(&mut self, bcx: &Builder<'a, 'tcx>, llindex: ValueRef) -> ValueRef { let index_size = machine::llbitsize_of_real(bcx.ccx, common::val_ty(llindex)); - let int_size = machine::llbitsize_of_real(bcx.ccx, ccx.int_type()); + let int_size = machine::llbitsize_of_real(bcx.ccx, bcx.ccx.int_type()); if index_size < int_size { - bcx.zext(llindex, ccx.int_type()) + bcx.zext(llindex, bcx.ccx.int_type()) } else if index_size > int_size { - bcx.trunc(llindex, ccx.int_type()) + bcx.trunc(llindex, bcx.ccx.int_type()) } else { llindex } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index a1373cb9482b..eedd7956805b 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -19,7 +19,8 @@ use rustc::infer::TransNormalize; use rustc::ty::TypeFoldable; use session::config::FullDebugInfo; use base; -use common::{self, BlockAndBuilder, CrateContext, FunctionContext, C_null, Funclet}; +use builder::Builder; +use common::{self, CrateContext, C_null, Funclet}; use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext}; use monomorphize::{self, Instance}; use abi::FnType; @@ -37,7 +38,7 @@ use rustc_data_structures::indexed_vec::{IndexVec, Idx}; pub use self::constant::trans_static_initializer; use self::analyze::CleanupKind; -use self::lvalue::{LvalueRef}; +use self::lvalue::LvalueRef; use rustc::mir::traversal; use self::operand::{OperandRef, OperandValue}; @@ -48,7 +49,7 @@ pub struct MirContext<'a, 'tcx:'a> { debug_context: debuginfo::FunctionDebugContext, - fcx: &'a common::FunctionContext<'a, 'tcx>, + llfn: ValueRef, ccx: &'a CrateContext<'a, 'tcx>, @@ -106,7 +107,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { monomorphize::apply_param_substs(self.ccx.shared(), self.param_substs, value) } - pub fn set_debug_loc(&mut self, bcx: &BlockAndBuilder, source_info: mir::SourceInfo) { + pub fn set_debug_loc(&mut self, bcx: &Builder, source_info: mir::SourceInfo) { let (scope, span) = self.debug_loc(source_info); debuginfo::set_source_location(&self.debug_context, bcx, scope, span); } @@ -198,7 +199,8 @@ impl<'tcx> LocalRef<'tcx> { /////////////////////////////////////////////////////////////////////////// pub fn trans_mir<'a, 'tcx: 'a>( - fcx: &'a FunctionContext<'a, 'tcx>, + ccx: &'a CrateContext<'a, 'tcx>, + llfn: ValueRef, fn_ty: FnType, mir: &'a Mir<'tcx>, instance: Instance<'tcx>, @@ -207,8 +209,8 @@ pub fn trans_mir<'a, 'tcx: 'a>( ) { debug!("fn_ty: {:?}", fn_ty); let debug_context = - debuginfo::create_function_debug_context(fcx.ccx, instance, sig, abi, fcx.llfn, mir); - let bcx = fcx.get_entry_block(); + debuginfo::create_function_debug_context(ccx, instance, sig, abi, llfn, mir); + let bcx = Builder::new_block(ccx, llfn, "entry-block"); let cleanup_kinds = analyze::cleanup_kinds(&mir); @@ -216,20 +218,20 @@ pub fn trans_mir<'a, 'tcx: 'a>( let block_bcxs: IndexVec = mir.basic_blocks().indices().map(|bb| { if bb == mir::START_BLOCK { - fcx.new_block("start") + bcx.build_sibling_block("start").llbb() } else { - fcx.new_block(&format!("{:?}", bb)) + bcx.build_sibling_block(&format!("{:?}", bb)).llbb() } }).collect(); // Compute debuginfo scopes from MIR scopes. - let scopes = debuginfo::create_mir_scopes(fcx, mir, &debug_context); + let scopes = debuginfo::create_mir_scopes(ccx, mir, &debug_context); let mut mircx = MirContext { mir: mir, - fcx: fcx, + llfn: llfn, fn_ty: fn_ty, - ccx: fcx.ccx, + ccx: ccx, llpersonalityslot: None, blocks: block_bcxs, unreachable_block: None, @@ -266,7 +268,9 @@ pub fn trans_mir<'a, 'tcx: 'a>( } debug!("alloc: {:?} ({}) -> lvalue", local, name); - let lvalue = LvalueRef::alloca(&bcx, ty, &name.as_str()); + assert!(!ty.has_erasable_regions()); + let lltemp = bcx.alloca_ty(ty, &name.as_str()); + let lvalue = LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty)); if dbg { let (scope, span) = mircx.debug_loc(source_info); declare_local(&bcx, &mircx.debug_context, name, ty, scope, @@ -278,11 +282,13 @@ pub fn trans_mir<'a, 'tcx: 'a>( // Temporary or return pointer if local == mir::RETURN_POINTER && mircx.fn_ty.ret.is_indirect() { debug!("alloc: {:?} (return pointer) -> lvalue", local); - let llretptr = llvm::get_param(fcx.llfn, 0); + let llretptr = llvm::get_param(llfn, 0); LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty))) } else if lvalue_locals.contains(local.index()) { debug!("alloc: {:?} -> lvalue", local); - LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", local))) + assert!(!ty.has_erasable_regions()); + let lltemp = bcx.alloca_ty(ty, &format!("{:?}", local)); + LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))) } else { // If this is an immediate local, we do not create an // alloca in advance. Instead we wait until we see the @@ -312,9 +318,9 @@ pub fn trans_mir<'a, 'tcx: 'a>( let funclets: IndexVec> = mircx.cleanup_kinds.iter_enumerated().map(|(bb, cleanup_kind)| { if let CleanupKind::Funclet = *cleanup_kind { - let bcx = mircx.build_block(bb); + let bcx = mircx.get_builder(bb); bcx.set_personality_fn(mircx.ccx.eh_personality()); - if base::wants_msvc_seh(fcx.ccx.sess()) { + if base::wants_msvc_seh(ccx.sess()) { return Some(Funclet::new(bcx.cleanup_pad(None, &[]))); } } @@ -347,13 +353,12 @@ pub fn trans_mir<'a, 'tcx: 'a>( /// Produce, for each argument, a `ValueRef` pointing at the /// argument's value. As arguments are lvalues, these are always /// indirect. -fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, +fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, mircx: &MirContext<'a, 'tcx>, scopes: &IndexVec, lvalue_locals: &BitVector) -> Vec> { let mir = mircx.mir; - let fcx = bcx.fcx(); let tcx = bcx.tcx(); let mut idx = 0; let mut llarg_idx = mircx.fn_ty.ret.is_indirect() as usize; @@ -381,7 +386,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, _ => bug!("spread argument isn't a tuple?!") }; - let lltemp = base::alloc_ty(&bcx, arg_ty, &format!("arg{}", arg_index)); + let lltemp = bcx.alloca_ty(arg_ty, &format!("arg{}", arg_index)); for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() { let dst = bcx.struct_gep(lltemp, i); let arg = &mircx.fn_ty.args[idx]; @@ -428,7 +433,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, if arg.pad.is_some() { llarg_idx += 1; } - let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); + let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); llarg_idx += 1; llarg } else if !lvalue_locals.contains(local.index()) && @@ -444,13 +449,13 @@ fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, if arg.pad.is_some() { llarg_idx += 1; } - let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); + let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); llarg_idx += 1; let val = if common::type_is_fat_ptr(bcx.ccx, arg_ty) { let meta = &mircx.fn_ty.args[idx]; idx += 1; assert_eq!((meta.cast, meta.pad), (None, None)); - let llmeta = llvm::get_param(fcx.llfn, llarg_idx as c_uint); + let llmeta = llvm::get_param(bcx.llfn(), llarg_idx as c_uint); llarg_idx += 1; OperandValue::Pair(llarg, llmeta) } else { @@ -462,7 +467,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, }; return LocalRef::Operand(Some(operand.unpack_if_pair(bcx))); } else { - let lltemp = base::alloc_ty(&bcx, arg_ty, &format!("arg{}", arg_index)); + let lltemp = bcx.alloca_ty(arg_ty, &format!("arg{}", arg_index)); if common::type_is_fat_ptr(bcx.ccx, arg_ty) { // we pass fat pointers as two words, but we want to // represent them internally as a pointer to two words, @@ -514,8 +519,8 @@ fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, // doesn't actually strip the offset when splitting the closure // environment into its components so it ends up out of bounds. let env_ptr = if !env_ref { - let alloc = bcx.fcx().alloca(common::val_ty(llval), "__debuginfo_env_ptr"); - bcx.store(llval, alloc); + let alloc = bcx.alloca(common::val_ty(llval), "__debuginfo_env_ptr"); + bcx.store(llval, alloc, None); alloc } else { llval @@ -573,7 +578,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, mod analyze; mod block; mod constant; -mod lvalue; +pub mod lvalue; mod operand; mod rvalue; mod statement; diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 526155655af2..28a247ee612a 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -14,7 +14,8 @@ use rustc::mir; use rustc_data_structures::indexed_vec::Idx; use base; -use common::{self, BlockAndBuilder}; +use common; +use builder::Builder; use value::Value; use type_of; use type_::Type; @@ -85,8 +86,7 @@ impl<'a, 'tcx> OperandRef<'tcx> { /// If this operand is a Pair, we return an /// Immediate aggregate with the two values. - pub fn pack_if_pair(mut self, bcx: &BlockAndBuilder<'a, 'tcx>) - -> OperandRef<'tcx> { + pub fn pack_if_pair(mut self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { if let OperandValue::Pair(a, b) = self.val { // Reconstruct the immediate aggregate. let llty = type_of::type_of(bcx.ccx, self.ty); @@ -107,8 +107,7 @@ impl<'a, 'tcx> OperandRef<'tcx> { /// If this operand is a pair in an Immediate, /// we return a Pair with the two halves. - pub fn unpack_if_pair(mut self, bcx: &BlockAndBuilder<'a, 'tcx>) - -> OperandRef<'tcx> { + pub fn unpack_if_pair(mut self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { if let OperandValue::Immediate(llval) = self.val { // Deconstruct the immediate aggregate. if common::type_is_imm_pair(bcx.ccx, self.ty) { @@ -136,7 +135,7 @@ impl<'a, 'tcx> OperandRef<'tcx> { impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_load(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, llval: ValueRef, ty: Ty<'tcx>) -> OperandRef<'tcx> @@ -165,7 +164,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } pub fn trans_consume(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, lvalue: &mir::Lvalue<'tcx>) -> OperandRef<'tcx> { @@ -217,7 +216,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } pub fn trans_operand(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, operand: &mir::Operand<'tcx>) -> OperandRef<'tcx> { @@ -242,23 +241,26 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } pub fn store_operand(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, lldest: ValueRef, - operand: OperandRef<'tcx>) { - debug!("store_operand: operand={:?}", operand); + operand: OperandRef<'tcx>, + align: Option) { + debug!("store_operand: operand={:?}, align={:?}", operand, align); // Avoid generating stores of zero-sized values, because the only way to have a zero-sized // value is through `undef`, and store itself is useless. if common::type_is_zero_size(bcx.ccx, operand.ty) { return; } match operand.val { - OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty), - OperandValue::Immediate(s) => base::store_ty(bcx, s, lldest, operand.ty), + OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty, align), + OperandValue::Immediate(s) => { + bcx.store(base::from_immediate(bcx, s), lldest, align); + } OperandValue::Pair(a, b) => { let a = base::from_immediate(bcx, a); let b = base::from_immediate(bcx, b); - bcx.store(a, bcx.struct_gep(lldest, 0)); - bcx.store(b, bcx.struct_gep(lldest, 1)); + bcx.store(a, bcx.struct_gep(lldest, 0), align); + bcx.store(b, bcx.struct_gep(lldest, 1), align); } } } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index cca48737bb96..1b97a8d010cf 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -12,13 +12,15 @@ use llvm::{self, ValueRef}; use rustc::ty::{self, Ty}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::layout::Layout; +use rustc::mir::tcx::LvalueTy; use rustc::mir; use middle::lang_items::ExchangeMallocFnLangItem; use asm; use base; +use builder::Builder; use callee::Callee; -use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder}; +use common::{self, val_ty, C_bool, C_null, C_uint}; use common::{C_integral}; use adt; use machine; @@ -35,10 +37,10 @@ use super::lvalue::{LvalueRef}; impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_rvalue(&mut self, - bcx: BlockAndBuilder<'a, 'tcx>, + bcx: Builder<'a, 'tcx>, dest: LvalueRef<'tcx>, rvalue: &mir::Rvalue<'tcx>) - -> BlockAndBuilder<'a, 'tcx> + -> Builder<'a, 'tcx> { debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})", Value(dest.llval), rvalue); @@ -48,7 +50,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let tr_operand = self.trans_operand(&bcx, operand); // FIXME: consider not copying constants through stack. (fixable by translating // constants into OperandValue::Ref, why don’t we do that yet if we don’t?) - self.store_operand(&bcx, dest.llval, tr_operand); + self.store_operand(&bcx, dest.llval, tr_operand, None); bcx } @@ -59,7 +61,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // into-coerce of a thin pointer to a fat pointer - just // use the operand path. let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - self.store_operand(&bcx, dest.llval, temp); + self.store_operand(&bcx, dest.llval, temp, None); return bcx; } @@ -79,7 +81,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // index into the struct, and this case isn't // important enough for it. debug!("trans_rvalue: creating ugly alloca"); - let lltemp = base::alloc_ty(&bcx, operand.ty, "__unsize_temp"); + let lltemp = bcx.alloca_ty(operand.ty, "__unsize_temp"); base::store_ty(&bcx, llval, lltemp, operand.ty); lltemp } @@ -95,13 +97,13 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let size = C_uint(bcx.ccx, size); let base = base::get_dataptr(&bcx, dest.llval); tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot| { - self.store_operand(bcx, llslot, tr_elem); + self.store_operand(bcx, llslot, tr_elem, None); }) } mir::Rvalue::Aggregate(ref kind, ref operands) => { match *kind { - mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { + mir::AggregateKind::Adt(adt_def, variant_index, substs, active_field_index) => { let disr = Disr::from(adt_def.variants[variant_index].disr_val); let dest_ty = dest.ty.to_ty(bcx.tcx()); adt::trans_set_discr(&bcx, dest_ty, dest.llval, Disr::from(disr)); @@ -109,11 +111,15 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let op = self.trans_operand(&bcx, operand); // Do not generate stores and GEPis for zero-sized fields. if !common::type_is_zero_size(bcx.ccx, op.ty) { - let val = adt::MaybeSizedValue::sized(dest.llval); + let mut val = LvalueRef::new_sized(dest.llval, dest.ty); let field_index = active_field_index.unwrap_or(i); - let lldest_i = adt::trans_field_ptr(&bcx, dest_ty, val, disr, - field_index); - self.store_operand(&bcx, lldest_i, op); + val.ty = LvalueTy::Downcast { + adt_def: adt_def, + substs: self.monomorphize(&substs), + variant_index: disr.0 as usize, + }; + let lldest_i = val.trans_field_ptr(&bcx, field_index); + self.store_operand(&bcx, lldest_i, op, None); } } }, @@ -138,7 +144,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { i }; let dest = bcx.gepi(dest.llval, &[0, i]); - self.store_operand(&bcx, dest, op); + self.store_operand(&bcx, dest, op, None); } } } @@ -163,16 +169,16 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { _ => { assert!(rvalue_creates_operand(rvalue)); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - self.store_operand(&bcx, dest.llval, temp); + self.store_operand(&bcx, dest.llval, temp, None); bcx } } } pub fn trans_rvalue_operand(&mut self, - bcx: BlockAndBuilder<'a, 'tcx>, + bcx: Builder<'a, 'tcx>, rvalue: &mir::Rvalue<'tcx>) - -> (BlockAndBuilder<'a, 'tcx>, OperandRef<'tcx>) + -> (Builder<'a, 'tcx>, OperandRef<'tcx>) { assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); @@ -254,22 +260,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); let ll_t_in = type_of::immediate_type_of(bcx.ccx, operand.ty); let ll_t_out = type_of::immediate_type_of(bcx.ccx, cast_ty); - let (llval, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in { - let l = bcx.ccx.layout_of(operand.ty); - let discr = match operand.val { - OperandValue::Immediate(llval) => llval, - OperandValue::Ref(llptr) => { - adt::trans_get_discr(&bcx, operand.ty, llptr, None, true) - } - OperandValue::Pair(..) => bug!("Unexpected Pair operand") - }; - let (signed, min, max) = match l { - &Layout::CEnum { signed, min, max, .. } => { - (signed, min, max) - } - _ => bug!("CEnum {:?} is not an enum", operand) - }; - + let llval = operand.immediate(); + let l = bcx.ccx.layout_of(operand.ty); + let signed = if let Layout::CEnum { signed, min, max, .. } = *l { if max > min { // We want `table[e as usize]` to not // have bound checks, and this is the most @@ -277,14 +270,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { base::call_assume(&bcx, bcx.icmp( llvm::IntULE, - discr, - C_integral(common::val_ty(discr), max, false) - )) + llval, + C_integral(common::val_ty(llval), max, false) + )); } - (discr, signed) + signed } else { - (operand.immediate(), operand.ty.is_signed()) + operand.ty.is_signed() }; let newval = match (r_t_in, r_t_out) { @@ -477,7 +470,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } pub fn trans_scalar_binop(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, op: mir::BinOp, lhs: ValueRef, rhs: ValueRef, @@ -552,7 +545,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } pub fn trans_fat_ptr_binop(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, op: mir::BinOp, lhs_addr: ValueRef, lhs_extra: ValueRef, @@ -599,7 +592,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } pub fn trans_scalar_checked_binop(&mut self, - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, op: mir::BinOp, lhs: ValueRef, rhs: ValueRef, @@ -681,7 +674,7 @@ enum OverflowOp { Add, Sub, Mul } -fn get_overflow_intrinsic(oop: OverflowOp, bcx: &BlockAndBuilder, ty: Ty) -> ValueRef { +fn get_overflow_intrinsic(oop: OverflowOp, bcx: &Builder, ty: Ty) -> ValueRef { use syntax::ast::IntTy::*; use syntax::ast::UintTy::*; use rustc::ty::{TyInt, TyUint}; diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index cc85f68c197e..48fc9720e4b8 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -11,7 +11,8 @@ use rustc::mir; use base; -use common::{self, BlockAndBuilder}; +use common; +use builder::Builder; use super::MirContext; use super::LocalRef; @@ -20,9 +21,9 @@ use super::super::disr::Disr; impl<'a, 'tcx> MirContext<'a, 'tcx> { pub fn trans_statement(&mut self, - bcx: BlockAndBuilder<'a, 'tcx>, + bcx: Builder<'a, 'tcx>, statement: &mir::Statement<'tcx>) - -> BlockAndBuilder<'a, 'tcx> { + -> Builder<'a, 'tcx> { debug!("trans_statement(statement={:?})", statement); self.set_debug_loc(&bcx, statement.source_info); @@ -77,10 +78,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } fn trans_storage_liveness(&self, - bcx: BlockAndBuilder<'a, 'tcx>, + bcx: Builder<'a, 'tcx>, lvalue: &mir::Lvalue<'tcx>, intrinsic: base::Lifetime) - -> BlockAndBuilder<'a, 'tcx> { + -> Builder<'a, 'tcx> { if let mir::Lvalue::Local(index) = *lvalue { if let LocalRef::Lvalue(tr_lval) = self.locals[index] { intrinsic.call(&bcx, tr_lval.llval); diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index d93bbec7efa4..d1d167306c1f 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -53,8 +53,6 @@ //! - One for "stable", that is non-generic, code //! - One for more "volatile" code, i.e. monomorphized instances of functions //! defined in that module -//! - Code for monomorphized instances of functions from external crates gets -//! placed into every codegen unit that uses that instance. //! //! In order to see why this heuristic makes sense, let's take a look at when a //! codegen unit can get invalidated: @@ -82,17 +80,6 @@ //! side-effect of references a little by at least not touching the non-generic //! code of the module. //! -//! As another optimization, monomorphized functions from external crates get -//! some special handling. Since we assume that the definition of such a -//! function changes rather infrequently compared to local items, we can just -//! instantiate external functions in every codegen unit where it is referenced -//! -- without having to fear that doing this will cause a lot of unnecessary -//! re-compilations. If such a reference is added or removed, the codegen unit -//! has to be re-translated anyway. -//! (Note that this only makes sense if external crates actually don't change -//! frequently. For certain multi-crate projects this might not be a valid -//! assumption). -//! //! A Note on Inlining //! ------------------ //! As briefly mentioned above, in order for LLVM to be able to inline a @@ -107,10 +94,9 @@ //! inlined, so it can distribute function instantiations accordingly. Since //! there is no way of knowing for sure which functions LLVM will decide to //! inline in the end, we apply a heuristic here: Only functions marked with -//! #[inline] and (as stated above) functions from external crates are -//! considered for inlining by the partitioner. The current implementation -//! will not try to determine if a function is likely to be inlined by looking -//! at the functions definition. +//! #[inline] are considered for inlining by the partitioner. The current +//! implementation will not try to determine if a function is likely to be +//! inlined by looking at the functions definition. //! //! Note though that as a side-effect of creating a codegen units per //! source-level module, functions from the same module will be available for @@ -133,7 +119,7 @@ use std::sync::Arc; use symbol_map::SymbolMap; use syntax::ast::NodeId; use syntax::symbol::{Symbol, InternedString}; -use trans_item::TransItem; +use trans_item::{TransItem, InstantiationMode}; use util::nodemap::{FxHashMap, FxHashSet}; pub enum PartitioningStrategy { @@ -326,13 +312,15 @@ fn place_root_translation_items<'a, 'tcx, I>(scx: &SharedCrateContext<'a, 'tcx>, let tcx = scx.tcx(); let mut roots = FxHashSet(); let mut codegen_units = FxHashMap(); + let is_incremental_build = tcx.sess.opts.incremental.is_some(); for trans_item in trans_items { - let is_root = !trans_item.is_instantiated_only_on_demand(tcx); + let is_root = trans_item.instantiation_mode(tcx) == InstantiationMode::GloballyShared; if is_root { let characteristic_def_id = characteristic_def_id_of_trans_item(scx, trans_item); - let is_volatile = trans_item.is_generic_fn(); + let is_volatile = is_incremental_build && + trans_item.is_generic_fn(); let codegen_unit_name = match characteristic_def_id { Some(def_id) => compute_codegen_unit_name(tcx, def_id, is_volatile), @@ -350,25 +338,9 @@ fn place_root_translation_items<'a, 'tcx, I>(scx: &SharedCrateContext<'a, 'tcx>, Some(explicit_linkage) => explicit_linkage, None => { match trans_item { + TransItem::Fn(..) | TransItem::Static(..) => llvm::ExternalLinkage, TransItem::DropGlue(..) => unreachable!(), - // Is there any benefit to using ExternalLinkage?: - TransItem::Fn(ref instance) => { - if instance.substs.types().next().is_none() { - // This is a non-generic functions, we always - // make it visible externally on the chance that - // it might be used in another codegen unit. - // Later on base::internalize_symbols() will - // assign "internal" linkage to those symbols - // that are not referenced from other codegen - // units (and are not publicly visible). - llvm::ExternalLinkage - } else { - // In the current setup, generic functions cannot - // be roots. - unreachable!() - } - } } } }; @@ -448,29 +420,14 @@ fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartit if let Some(linkage) = codegen_unit.items.get(&trans_item) { // This is a root, just copy it over new_codegen_unit.items.insert(trans_item, *linkage); - } else if initial_partitioning.roots.contains(&trans_item) { - // This item will be instantiated in some other codegen unit, - // so we just add it here with AvailableExternallyLinkage - // FIXME(mw): I have not seen it happening yet but having - // available_externally here could potentially lead - // to the same problem with exception handling tables - // as in the case below. - new_codegen_unit.items.insert(trans_item, - llvm::AvailableExternallyLinkage); - } else if trans_item.is_from_extern_crate() && !trans_item.is_generic_fn() { - // FIXME(mw): It would be nice if we could mark these as - // `AvailableExternallyLinkage`, since they should have - // been instantiated in the extern crate. But this - // sometimes leads to crashes on Windows because LLVM - // does not handle exception handling table instantiation - // reliably in that case. - new_codegen_unit.items.insert(trans_item, llvm::InternalLinkage); } else { - // We can't be sure if this will also be instantiated - // somewhere else, so we add an instance here with - // InternalLinkage so we don't get any conflicts. - new_codegen_unit.items.insert(trans_item, - llvm::InternalLinkage); + if initial_partitioning.roots.contains(&trans_item) { + bug!("GloballyShared trans-item inlined into other CGU: \ + {:?}", trans_item); + } + + // This is a cgu-private copy + new_codegen_unit.items.insert(trans_item, llvm::InternalLinkage); } } diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index f6f91411225d..816c34425437 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -45,6 +45,18 @@ pub enum TransItem<'tcx> { Static(NodeId) } +/// Describes how a translation item will be instantiated in object files. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +pub enum InstantiationMode { + /// There will be exactly one instance of the given TransItem. It will have + /// external linkage so that it can be linked to from other codegen units. + GloballyShared, + + /// Each codegen unit containing a reference to the given TransItem will + /// have its own private copy of the function (with internal linkage). + LocalCopy, +} + impl<'a, 'tcx> TransItem<'tcx> { pub fn define(&self, ccx: &CrateContext<'a, 'tcx>) { @@ -231,22 +243,21 @@ impl<'a, 'tcx> TransItem<'tcx> { } } - /// True if the translation item should only be translated to LLVM IR if - /// it is referenced somewhere (like inline functions, for example). - pub fn is_instantiated_only_on_demand(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool { - if self.explicit_linkage(tcx).is_some() { - return false; - } - + pub fn instantiation_mode(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>) + -> InstantiationMode { match *self { TransItem::Fn(ref instance) => { - !instance.def.is_local() || - instance.substs.types().next().is_some() || - common::is_closure(tcx, instance.def) || - attr::requests_inline(&tcx.get_attrs(instance.def)[..]) + if self.explicit_linkage(tcx).is_none() && + (common::is_closure(tcx, instance.def) || + attr::requests_inline(&tcx.get_attrs(instance.def)[..])) { + InstantiationMode::LocalCopy + } else { + InstantiationMode::GloballyShared + } } - TransItem::DropGlue(..) => true, - TransItem::Static(..) => false, + TransItem::DropGlue(..) => InstantiationMode::LocalCopy, + TransItem::Static(..) => InstantiationMode::GloballyShared, } } @@ -260,18 +271,6 @@ impl<'a, 'tcx> TransItem<'tcx> { } } - /// Returns true if there has to be a local copy of this TransItem in every - /// codegen unit that references it (as with inlined functions, for example) - pub fn needs_local_copy(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool { - // Currently everything that is instantiated only on demand is done so - // with "internal" linkage, so we need a copy to be present in every - // codegen unit. - // This is coincidental: We could also instantiate something only if it - // is referenced (e.g. a regular, private function) but place it in its - // own codegen unit with "external" linkage. - self.is_instantiated_only_on_demand(tcx) - } - pub fn explicit_linkage(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option { let def_id = match *self { TransItem::Fn(ref instance) => instance.def, diff --git a/src/librustc_trans/tvec.rs b/src/librustc_trans/tvec.rs index c09726fda081..cbcbb02bdc89 100644 --- a/src/librustc_trans/tvec.rs +++ b/src/librustc_trans/tvec.rs @@ -9,28 +9,29 @@ // except according to those terms. use llvm; +use builder::Builder; use llvm::ValueRef; use common::*; use rustc::ty::Ty; pub fn slice_for_each<'a, 'tcx, F>( - bcx: &BlockAndBuilder<'a, 'tcx>, + bcx: &Builder<'a, 'tcx>, data_ptr: ValueRef, unit_ty: Ty<'tcx>, len: ValueRef, f: F -) -> BlockAndBuilder<'a, 'tcx> where F: FnOnce(&BlockAndBuilder<'a, 'tcx>, ValueRef) { +) -> Builder<'a, 'tcx> where F: FnOnce(&Builder<'a, 'tcx>, ValueRef) { // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890) let zst = type_is_zero_size(bcx.ccx, unit_ty); - let add = |bcx: &BlockAndBuilder, a, b| if zst { + let add = |bcx: &Builder, a, b| if zst { bcx.add(a, b) } else { bcx.inbounds_gep(a, &[b]) }; - let body_bcx = bcx.fcx().build_new_block("slice_loop_body"); - let next_bcx = bcx.fcx().build_new_block("slice_loop_next"); - let header_bcx = bcx.fcx().build_new_block("slice_loop_header"); + let body_bcx = bcx.build_sibling_block("slice_loop_body"); + let next_bcx = bcx.build_sibling_block("slice_loop_next"); + let header_bcx = bcx.build_sibling_block("slice_loop_header"); let start = if zst { C_uint(bcx.ccx, 0usize) diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 75c25a605af2..bc984949fc69 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1660,8 +1660,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { self.associated_path_def_to_ty(ast_ty.id, ast_ty.span, ty, def, segment).0 } hir::TyArray(ref ty, length) => { - let e = &tcx.map.body(length).value; - if let Ok(length) = eval_length(tcx.global_tcx(), e, "array length") { + if let Ok(length) = eval_length(tcx.global_tcx(), length, "array length") { tcx.mk_array(self.ast_ty_to_ty(rscope, &ty), length) } else { self.tcx().types.err diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 9b86196b3ece..99c664cebeae 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -27,9 +27,17 @@ use syntax_pos::Span; impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn check_pat(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>) { + self.check_pat_arg(pat, expected, false); + } + + /// The `is_arg` argument indicates whether this pattern is the + /// *outermost* pattern in an argument (e.g., in `fn foo(&x: + /// &u32)`, it is true for the `&x` pattern but not `x`). This is + /// used to tailor error reporting. + pub fn check_pat_arg(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>, is_arg: bool) { let tcx = self.tcx; - debug!("check_pat(pat={:?},expected={:?})", pat, expected); + debug!("check_pat(pat={:?},expected={:?},is_arg={})", pat, expected, is_arg); let ty = match pat.node { PatKind::Wild => { @@ -202,6 +210,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // can, to avoid creating needless variables. This // also helps with the bad interactions of the given // hack detailed in (*) below. + debug!("check_pat_arg: expected={:?}", expected); let (rptr_ty, inner_ty) = match expected.sty { ty::TyRef(_, mt) if mt.mutbl == mutbl => { (expected, mt.ty) @@ -212,7 +221,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl }; let region = self.next_region_var(infer::PatternRegion(pat.span)); let rptr_ty = tcx.mk_ref(region, mt); - self.demand_eqtype(pat.span, expected, rptr_ty); + debug!("check_pat_arg: demanding {:?} = {:?}", expected, rptr_ty); + let err = self.demand_eqtype_diag(pat.span, expected, rptr_ty); + + // Look for a case like `fn foo(&foo: u32)` and suggest + // `fn foo(foo: &u32)` + if let Some(mut err) = err { + if is_arg { + if let PatKind::Binding(..) = inner.node { + if let Ok(snippet) = self.sess().codemap() + .span_to_snippet(pat.span) + { + err.help(&format!("did you mean `{}: &{}`?", + &snippet[1..], + expected)); + } + } + } + err.emit(); + } (rptr_ty, inner_ty) } }; @@ -399,7 +426,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.check_pat(&p, discrim_ty); all_pats_diverge &= self.diverges.get(); } - all_pats_diverge + // As discussed with @eddyb, this is for disabling unreachable_code + // warnings on patterns (they're now subsumed by unreachable_patterns + // warnings). + match all_pats_diverge { + Diverges::Maybe => Diverges::Maybe, + Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways, + } }).collect(); // Now typecheck the blocks. diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 9412c9105c7a..0551887e2e8a 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -99,10 +99,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn_ty.sig, opt_kind); - self.tables.borrow_mut().closure_tys.insert(expr_def_id, fn_ty); + self.tables.borrow_mut().closure_tys.insert(expr.id, fn_ty); match opt_kind { Some(kind) => { - self.tables.borrow_mut().closure_kinds.insert(expr_def_id, kind); + self.tables.borrow_mut().closure_kinds.insert(expr.id, kind); } None => {} } diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index be10b77bd8b9..3960dc9edbd2 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -227,7 +227,7 @@ fn compare_predicate_entailment<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, trait_param_env, normalize_cause.clone()); - tcx.infer_ctxt(None, Some(trait_param_env), Reveal::NotSpecializable).enter(|infcx| { + tcx.infer_ctxt(trait_param_env, Reveal::NotSpecializable).enter(|infcx| { let inh = Inherited::new(ccx, infcx); let infcx = &inh.infcx; let fulfillment_cx = &inh.fulfillment_cx; @@ -747,7 +747,7 @@ pub fn compare_const_impl<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); let tcx = ccx.tcx; - tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| { + tcx.infer_ctxt((), Reveal::NotSpecializable).enter(|infcx| { let mut fulfillment_cx = traits::FulfillmentContext::new(); // The below is for the most part highly similar to the procedure diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 393d9341a084..a77887cd221d 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -19,6 +19,7 @@ use syntax_pos::{self, Span}; use rustc::hir; use rustc::hir::def::Def; use rustc::ty::{self, AssociatedItem}; +use errors::DiagnosticBuilder; use super::method::probe; @@ -38,20 +39,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { - self.demand_eqtype_with_origin(&self.misc(sp), expected, actual); + if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) { + err.emit(); + } + } + + pub fn demand_eqtype_diag(&self, + sp: Span, + expected: Ty<'tcx>, + actual: Ty<'tcx>) -> Option> { + self.demand_eqtype_with_origin(&self.misc(sp), expected, actual) } pub fn demand_eqtype_with_origin(&self, cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, - actual: Ty<'tcx>) - { + actual: Ty<'tcx>) -> Option> { match self.eq_types(false, cause, actual, expected) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); + None }, Err(e) => { - self.report_mismatched_types(cause, expected, actual, e).emit(); + Some(self.report_mismatched_types(cause, expected, actual, e)) } } } @@ -71,15 +81,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); if suggestions.len() > 0 { err.help(&format!("here are some functions which \ - might fulfill your needs:\n - {}", - self.get_best_match(&suggestions))); + might fulfill your needs:\n{}", + self.get_best_match(&suggestions).join("\n"))); }; err.emit(); } } fn format_method_suggestion(&self, method: &AssociatedItem) -> String { - format!(".{}({})", + format!("- .{}({})", method.name, if self.has_no_input_arg(method) { "" @@ -88,15 +98,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }) } - fn display_suggested_methods(&self, methods: &[AssociatedItem]) -> String { + fn display_suggested_methods(&self, methods: &[AssociatedItem]) -> Vec { methods.iter() .take(5) .map(|method| self.format_method_suggestion(&*method)) .collect::>() - .join("\n - ") } - fn get_best_match(&self, methods: &[AssociatedItem]) -> String { + fn get_best_match(&self, methods: &[AssociatedItem]) -> Vec { let no_argument_methods: Vec<_> = methods.iter() .filter(|ref x| self.has_no_input_arg(&*x)) diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index e13c4ea314f0..9cd54bd94c81 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -81,7 +81,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( // check that the impl type can be made to match the trait type. let impl_param_env = ty::ParameterEnvironment::for_item(tcx, self_type_node_id); - tcx.infer_ctxt(None, Some(impl_param_env), Reveal::NotSpecializable).enter(|infcx| { + tcx.infer_ctxt(impl_param_env, Reveal::NotSpecializable).enter(|infcx| { let tcx = infcx.tcx; let mut fulfillment_cx = traits::FulfillmentContext::new(); diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 5c3da4237bef..f331c561f0c4 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -801,13 +801,19 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // If so, add "synthetic impls". let steps = self.steps.clone(); for step in steps.iter() { - let closure_def_id = match step.self_ty.sty { - ty::TyClosure(a, _) => a, + let closure_id = match step.self_ty.sty { + ty::TyClosure(def_id, _) => { + if let Some(id) = self.tcx.map.as_local_node_id(def_id) { + id + } else { + continue; + } + } _ => continue, }; let closure_kinds = &self.tables.borrow().closure_kinds; - let closure_kind = match closure_kinds.get(&closure_def_id) { + let closure_kind = match closure_kinds.get(&closure_id) { Some(&k) => k, None => { return Err(MethodError::ClosureAmbiguity(trait_def_id)); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 26dd53fecb24..0b316c5d3752 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -105,7 +105,7 @@ use lint; use util::common::{ErrorReported, indenter}; use util::nodemap::{DefIdMap, FxHashMap, FxHashSet, NodeMap}; -use std::cell::{Cell, Ref, RefCell}; +use std::cell::{Cell, RefCell}; use std::cmp; use std::mem::replace; use std::ops::{self, Deref}; @@ -483,12 +483,11 @@ pub struct InheritedBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { impl<'a, 'gcx, 'tcx> CrateCtxt<'a, 'gcx> { pub fn inherited(&'a self, id: ast::NodeId) -> InheritedBuilder<'a, 'gcx, 'tcx> { + let tables = ty::Tables::empty(); let param_env = ParameterEnvironment::for_item(self.tcx, id); InheritedBuilder { ccx: self, - infcx: self.tcx.infer_ctxt(Some(ty::Tables::empty()), - Some(param_env), - Reveal::NotSpecializable) + infcx: self.tcx.infer_ctxt((tables, param_env), Reveal::NotSpecializable) } } } @@ -612,8 +611,7 @@ pub fn check_item_bodies(ccx: &CrateCtxt) -> CompileResult { let _task = ccx.tcx.dep_graph.in_task(DepNode::TypeckItemBody(def_id)); let param_env = ParameterEnvironment::for_item(ccx.tcx, item_id); - ccx.tcx.infer_ctxt(None, Some(param_env), - Reveal::NotSpecializable).enter(|infcx| { + ccx.tcx.infer_ctxt(param_env, Reveal::NotSpecializable).enter(|infcx| { let mut fulfillment_cx = traits::FulfillmentContext::new(); for obligation in obligations.iter().map(|o| o.to_obligation()) { fulfillment_cx.register_predicate_obligation(&infcx, obligation); @@ -681,7 +679,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, fcx.select_all_obligations_or_error(); // Casts can introduce new obligations. fcx.regionck_fn(fn_id, body); - fcx.resolve_type_vars_in_body(body, fn_id); + fcx.resolve_type_vars_in_body(body); }); } @@ -798,7 +796,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, fcx.register_old_wf_obligation(arg_ty, arg.pat.span, traits::MiscObligation); // Check the pattern. - fcx.check_pat(&arg.pat, arg_ty); + fcx.check_pat_arg(&arg.pat, arg_ty, true); fcx.write_ty(arg.id, arg_ty); } @@ -1248,7 +1246,7 @@ fn check_const_with_type<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, fcx.select_all_obligations_or_error(); fcx.regionck_expr(body); - fcx.resolve_type_vars_in_body(body, id); + fcx.resolve_type_vars_in_body(body); }); } @@ -1871,17 +1869,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - pub fn item_substs(&self) -> Ref>> { - // NOTE: @jroesch this is hack that appears to be fixed on nightly, will monitor if - // it changes when we upgrade the snapshot compiler - fn project_item_susbts<'a, 'tcx>(tables: &'a ty::Tables<'tcx>) - -> &'a NodeMap> { - &tables.item_substs - } - - Ref::map(self.tables.borrow(), project_item_susbts) - } - pub fn opt_node_ty_substs(&self, id: ast::NodeId, f: F) where @@ -3552,19 +3539,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { hir::UnNot => { oprnd_t = self.structurally_resolved_type(oprnd.span, oprnd_t); + let result = self.check_user_unop("!", "not", + tcx.lang_items.not_trait(), + expr, &oprnd, oprnd_t, unop); + // If it's builtin, we can reuse the type, this helps inference. if !(oprnd_t.is_integral() || oprnd_t.sty == ty::TyBool) { - oprnd_t = self.check_user_unop("!", "not", - tcx.lang_items.not_trait(), - expr, &oprnd, oprnd_t, unop); + oprnd_t = result; } } hir::UnNeg => { oprnd_t = self.structurally_resolved_type(oprnd.span, oprnd_t); + let result = self.check_user_unop("-", "neg", + tcx.lang_items.neg_trait(), + expr, &oprnd, oprnd_t, unop); + // If it's builtin, we can reuse the type, this helps inference. if !(oprnd_t.is_integral() || oprnd_t.is_fp()) { - oprnd_t = self.check_user_unop("-", "neg", - tcx.lang_items.neg_trait(), - expr, &oprnd, oprnd_t, unop); + oprnd_t = result; } } } @@ -3868,8 +3859,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { tcx.mk_array(unified, args.len()) } hir::ExprRepeat(ref element, count) => { - let count_expr = &tcx.map.body(count).value; - let count = eval_length(self.tcx.global_tcx(), count_expr, "repeat count") + let count = eval_length(self.tcx.global_tcx(), count, "repeat count") .unwrap_or(0); let uty = match expected { @@ -4561,7 +4551,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Check provided lifetime parameters. let lifetime_defs = segment.map_or(&[][..], |(_, generics)| &generics.regions); if lifetimes.len() > lifetime_defs.len() { - let span = lifetimes[lifetime_defs.len()].span; struct_span_err!(self.tcx.sess, span, E0088, "too many lifetime parameters provided: \ expected {}, found {}", @@ -4570,6 +4559,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .span_label(span, &format!("unexpected lifetime parameter{}", match lifetimes.len() { 1 => "", _ => "s" })) .emit(); + } else if lifetimes.len() > 0 && lifetimes.len() < lifetime_defs.len() { + struct_span_err!(self.tcx.sess, span, E0090, + "too few lifetime parameters provided: \ + expected {}, found {}", + count(lifetime_defs.len()), + count(lifetimes.len())) + .span_label(span, &format!("too few lifetime parameters")) + .emit(); } // The case where there is not enough lifetime parameters is not checked, diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 5d927a503a11..95da5a97f675 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -106,8 +106,7 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> { expr: &hir::Expr, capture_clause: hir::CaptureClause) { - let closure_def_id = self.fcx.tcx.map.local_def_id(expr.id); - if !self.fcx.tables.borrow().closure_kinds.contains_key(&closure_def_id) { + if !self.fcx.tables.borrow().closure_kinds.contains_key(&expr.id) { self.temp_closure_kinds.insert(expr.id, ty::ClosureKind::Fn); debug!("check_closure: adding closure {:?} as Fn", expr.id); } @@ -211,8 +210,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { // main table and process any deferred resolutions. let closure_def_id = self.fcx.tcx.map.local_def_id(id); if let Some(&kind) = self.temp_closure_kinds.get(&id) { - self.fcx.tables.borrow_mut().closure_kinds - .insert(closure_def_id, kind); + self.fcx.tables.borrow_mut().closure_kinds.insert(id, kind); debug!("closure_kind({:?}) = {:?}", closure_def_id, kind); let mut deferred_call_resolutions = diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index c80db7fa4d0e..4c124cdd60c0 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -505,7 +505,9 @@ impl<'ccx, 'gcx> CheckTypeWellFormedVisitor<'ccx, 'gcx> { debug!("check_method_receiver: receiver ty = {:?}", rcvr_ty); let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver); - fcx.demand_eqtype_with_origin(&cause, rcvr_ty, self_arg_ty); + if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, rcvr_ty, self_arg_ty) { + err.emit(); + } } fn check_variances_for_type_defn(&self, diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 9a2bfbf715af..02ac7c196b5c 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -33,10 +33,12 @@ use rustc::hir; // Entry point impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { - pub fn resolve_type_vars_in_body(&self, - body: &'gcx hir::Body, - item_id: ast::NodeId) { + pub fn resolve_type_vars_in_body(&self, body: &'gcx hir::Body) { assert_eq!(self.writeback_errors.get(), false); + + let item_id = self.tcx.map.body_owner(body.id()); + let item_def_id = self.tcx.map.local_def_id(item_id); + let mut wbcx = WritebackCx::new(self); for arg in &body.arguments { wbcx.visit_node_id(ResolvingPattern(arg.pat.span), arg.id); @@ -49,6 +51,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { wbcx.visit_anon_types(); wbcx.visit_deferred_obligations(item_id); wbcx.visit_type_nodes(); + + let tables = self.tcx.alloc_tables(wbcx.tables); + self.tcx.tables.borrow_mut().insert(item_def_id, tables); } } @@ -63,6 +68,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { struct WritebackCx<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>, + tables: ty::Tables<'gcx>, + // Mapping from free regions of the function to the // early-bound versions of them, visible from the // outside of the function. This is needed by, and @@ -74,6 +81,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { fn new(fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>) -> WritebackCx<'cx, 'gcx, 'tcx> { let mut wbcx = WritebackCx { fcx: fcx, + tables: ty::Tables::empty(), free_to_bound_regions: DefIdMap() }; @@ -113,18 +121,27 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { self.fcx.tcx } - fn write_ty_to_tcx(&self, node_id: ast::NodeId, ty: Ty<'gcx>) { - debug!("write_ty_to_tcx({}, {:?})", node_id, ty); + fn write_ty_to_tables(&mut self, node_id: ast::NodeId, ty: Ty<'gcx>) { + debug!("write_ty_to_tables({}, {:?})", node_id, ty); assert!(!ty.needs_infer()); - self.tcx().tables.borrow_mut().node_types.insert(node_id, ty); + self.tables.node_types.insert(node_id, ty); } // Hacky hack: During type-checking, we treat *all* operators // as potentially overloaded. But then, during writeback, if // we observe that something like `a+b` is (known to be) // operating on scalars, we clear the overload. - fn fix_scalar_binary_expr(&mut self, e: &hir::Expr) { + fn fix_scalar_builtin_expr(&mut self, e: &hir::Expr) { match e.node { + hir::ExprUnary(hir::UnNeg, ref inner) | + hir::ExprUnary(hir::UnNot, ref inner) => { + let inner_ty = self.fcx.node_ty(inner.id); + let inner_ty = self.fcx.resolve_type_vars_if_possible(&inner_ty); + + if inner_ty.is_scalar() { + self.fcx.tables.borrow_mut().method_map.remove(&MethodCall::expr(e.id)); + } + } hir::ExprBinary(ref op, ref lhs, ref rhs) | hir::ExprAssignOp(ref op, ref lhs, ref rhs) => { let lhs_ty = self.fcx.node_ty(lhs.id); @@ -185,7 +202,7 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> { return; } - self.fix_scalar_binary_expr(e); + self.fix_scalar_builtin_expr(e); self.visit_node_id(ResolvingExpr(e.span), e.id); self.visit_method_map_entry(ResolvingExpr(e.span), @@ -219,11 +236,6 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> { self.visit_node_id(ResolvingPattern(p.span), p.id); - debug!("Type for pattern binding {} (id {}) resolved to {:?}", - self.tcx().map.node_to_pretty_string(p.id), - p.id, - self.tcx().tables().node_id_to_type(p.id)); - intravisit::walk_pat(self, p); } @@ -234,13 +246,13 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> { let var_ty = self.fcx.local_ty(l.span, l.id); let var_ty = self.resolve(&var_ty, ResolvingLocal(l.span)); - self.write_ty_to_tcx(l.id, var_ty); + self.write_ty_to_tables(l.id, var_ty); intravisit::walk_local(self, l); } } impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { - fn visit_upvar_borrow_map(&self) { + fn visit_upvar_borrow_map(&mut self) { if self.fcx.writeback_errors.get() { return; } @@ -258,11 +270,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { debug!("Upvar capture for {:?} resolved to {:?}", upvar_id, new_upvar_capture); - self.tcx() - .tables - .borrow_mut() - .upvar_capture_map - .insert(*upvar_id, new_upvar_capture); + self.tables.upvar_capture_map.insert(*upvar_id, new_upvar_capture); } } @@ -271,13 +279,15 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { return } - for (def_id, closure_ty) in self.fcx.tables.borrow().closure_tys.iter() { - let closure_ty = self.resolve(closure_ty, ResolvingClosure(*def_id)); - self.tcx().tables.borrow_mut().closure_tys.insert(*def_id, closure_ty); + for (&id, closure_ty) in self.fcx.tables.borrow().closure_tys.iter() { + let closure_ty = self.resolve(closure_ty, ResolvingClosure(id)); + let def_id = self.tcx().map.local_def_id(id); + self.tcx().closure_tys.borrow_mut().insert(def_id, closure_ty); } - for (def_id, &closure_kind) in self.fcx.tables.borrow().closure_kinds.iter() { - self.tcx().tables.borrow_mut().closure_kinds.insert(*def_id, closure_kind); + for (&id, &closure_kind) in self.fcx.tables.borrow().closure_kinds.iter() { + let def_id = self.tcx().map.local_def_id(id); + self.tcx().closure_kinds.borrow_mut().insert(def_id, closure_kind); } } @@ -330,10 +340,10 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } } - fn visit_node_id(&self, reason: ResolveReason, id: ast::NodeId) { + fn visit_node_id(&mut self, reason: ResolveReason, id: ast::NodeId) { // Export associated path extensions. if let Some(def) = self.fcx.tables.borrow_mut().type_relative_path_defs.remove(&id) { - self.tcx().tables.borrow_mut().type_relative_path_defs.insert(id, def); + self.tables.type_relative_path_defs.insert(id, def); } // Resolve any borrowings for the node with id `id` @@ -342,7 +352,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { // Resolve the type of the node with id `id` let n_ty = self.fcx.node_ty(id); let n_ty = self.resolve(&n_ty, reason); - self.write_ty_to_tcx(id, n_ty); + self.write_ty_to_tables(id, n_ty); debug!("Node {} has type {:?}", id, n_ty); // Resolve any substitutions @@ -351,12 +361,12 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { if !item_substs.is_noop() { debug!("write_substs_to_tcx({}, {:?})", id, item_substs); assert!(!item_substs.substs.needs_infer()); - self.tcx().tables.borrow_mut().item_substs.insert(id, item_substs); + self.tables.item_substs.insert(id, item_substs); } }); } - fn visit_adjustments(&self, reason: ResolveReason, id: ast::NodeId) { + fn visit_adjustments(&mut self, reason: ResolveReason, id: ast::NodeId) { let adjustments = self.fcx.tables.borrow_mut().adjustments.remove(&id); match adjustments { None => { @@ -399,13 +409,12 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { target: self.resolve(&adjustment.target, reason) }; debug!("Adjustments for node {}: {:?}", id, resolved_adjustment); - self.tcx().tables.borrow_mut().adjustments.insert( - id, resolved_adjustment); + self.tables.adjustments.insert(id, resolved_adjustment); } } } - fn visit_method_map_entry(&self, + fn visit_method_map_entry(&mut self, reason: ResolveReason, method_call: MethodCall) { // Resolve any method map entry @@ -427,25 +436,25 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { //NB(jroesch): We need to match twice to avoid a double borrow which would cause an ICE if let Some(method) = new_method { - self.tcx().tables.borrow_mut().method_map.insert(method_call, method); + self.tables.method_map.insert(method_call, method); } } - fn visit_liberated_fn_sigs(&self) { + fn visit_liberated_fn_sigs(&mut self) { for (&node_id, fn_sig) in self.fcx.tables.borrow().liberated_fn_sigs.iter() { let fn_sig = self.resolve(fn_sig, ResolvingFnSig(node_id)); - self.tcx().tables.borrow_mut().liberated_fn_sigs.insert(node_id, fn_sig.clone()); + self.tables.liberated_fn_sigs.insert(node_id, fn_sig.clone()); } } - fn visit_fru_field_types(&self) { + fn visit_fru_field_types(&mut self) { for (&node_id, ftys) in self.fcx.tables.borrow().fru_field_types.iter() { let ftys = self.resolve(ftys, ResolvingFieldTypes(node_id)); - self.tcx().tables.borrow_mut().fru_field_types.insert(node_id, ftys); + self.tables.fru_field_types.insert(node_id, ftys); } } - fn visit_deferred_obligations(&self, item_id: ast::NodeId) { + fn visit_deferred_obligations(&mut self, item_id: ast::NodeId) { let deferred_obligations = self.fcx.deferred_obligations.borrow(); let obligations: Vec<_> = deferred_obligations.iter().map(|obligation| { let reason = ResolvingDeferredObligation(obligation.cause.span); @@ -487,7 +496,7 @@ enum ResolveReason { ResolvingLocal(Span), ResolvingPattern(Span), ResolvingUpvar(ty::UpvarId), - ResolvingClosure(DefId), + ResolvingClosure(ast::NodeId), ResolvingFnSig(ast::NodeId), ResolvingFieldTypes(ast::NodeId), ResolvingAnonTy(DefId), @@ -504,12 +513,12 @@ impl<'a, 'gcx, 'tcx> ResolveReason { ResolvingUpvar(upvar_id) => { tcx.expr_span(upvar_id.closure_expr_id) } + ResolvingClosure(id) | ResolvingFnSig(id) | ResolvingFieldTypes(id) | ResolvingTyNode(id) => { tcx.map.span(id) } - ResolvingClosure(did) | ResolvingAnonTy(did) => { tcx.def_span(did) } diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs new file mode 100644 index 000000000000..57df0fb2cee9 --- /dev/null +++ b/src/librustc_typeck/coherence/builtin.rs @@ -0,0 +1,349 @@ +// 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. + +//! Check properties that are required by built-in traits and set +//! up data structures required by type-checking/translation. + +use rustc::middle::free_region::FreeRegionMap; +use rustc::middle::lang_items::UnsizeTraitLangItem; + +use rustc::traits::{self, ObligationCause, Reveal}; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::ParameterEnvironment; +use rustc::ty::TypeFoldable; +use rustc::ty::subst::Subst; +use rustc::ty::util::CopyImplementationError; +use rustc::infer; + +use rustc::hir::def_id::DefId; +use rustc::hir::map as hir_map; +use rustc::hir::{self, ItemImpl}; + +pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + check_trait(tcx, tcx.lang_items.drop_trait(), visit_implementation_of_drop); + check_trait(tcx, tcx.lang_items.copy_trait(), visit_implementation_of_copy); + check_trait( + tcx, + tcx.lang_items.coerce_unsized_trait(), + visit_implementation_of_coerce_unsized); +} + +fn check_trait<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + trait_def_id: Option, + mut f: F) + where F: FnMut(TyCtxt<'a, 'tcx, 'tcx>, DefId, DefId) +{ + if let Some(trait_def_id) = trait_def_id { + let mut impls = vec![]; + tcx.lookup_trait_def(trait_def_id).for_each_impl(tcx, |did| { + impls.push(did); + }); + impls.sort(); + for impl_def_id in impls { + f(tcx, trait_def_id, impl_def_id); + } + } +} + +fn visit_implementation_of_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + _drop_did: DefId, + impl_did: DefId) { + let items = tcx.associated_item_def_ids(impl_did); + if items.is_empty() { + // We'll error out later. For now, just don't ICE. + return; + } + let method_def_id = items[0]; + + let self_type = tcx.item_type(impl_did); + match self_type.sty { + ty::TyAdt(type_def, _) => { + type_def.set_destructor(method_def_id); + } + _ => { + // Destructors only work on nominal types. + if let Some(impl_node_id) = tcx.map.as_local_node_id(impl_did) { + match tcx.map.find(impl_node_id) { + Some(hir_map::NodeItem(item)) => { + let span = match item.node { + ItemImpl(.., ref ty, _) => ty.span, + _ => item.span, + }; + struct_span_err!(tcx.sess, + span, + E0120, + "the Drop trait may only be implemented on \ + structures") + .span_label(span, &format!("implementing Drop requires a struct")) + .emit(); + } + _ => { + bug!("didn't find impl in ast map"); + } + } + } else { + bug!("found external impl of Drop trait on \ + something other than a struct"); + } + } + } +} + +fn visit_implementation_of_copy<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + _copy_did: DefId, + impl_did: DefId) { + debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); + + let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) { + n + } else { + debug!("visit_implementation_of_copy(): impl not in this \ + crate"); + return; + }; + + let self_type = tcx.item_type(impl_did); + debug!("visit_implementation_of_copy: self_type={:?} (bound)", + self_type); + + let span = tcx.map.span(impl_node_id); + let param_env = ParameterEnvironment::for_item(tcx, impl_node_id); + let self_type = self_type.subst(tcx, ¶m_env.free_substs); + assert!(!self_type.has_escaping_regions()); + + debug!("visit_implementation_of_copy: self_type={:?} (free)", + self_type); + + match param_env.can_type_implement_copy(tcx, self_type, span) { + Ok(()) => {} + Err(CopyImplementationError::InfrigingField(field)) => { + let item = tcx.map.expect_item(impl_node_id); + let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node { + tr.path.span + } else { + span + }; + + struct_span_err!(tcx.sess, + span, + E0204, + "the trait `Copy` may not be implemented for this type") + .span_label( + tcx.def_span(field.did), + &"this field does not implement `Copy`") + .emit() + } + Err(CopyImplementationError::NotAnAdt) => { + let item = tcx.map.expect_item(impl_node_id); + let span = if let ItemImpl(.., ref ty, _) = item.node { + ty.span + } else { + span + }; + + struct_span_err!(tcx.sess, + span, + E0206, + "the trait `Copy` may not be implemented for this type") + .span_label(span, &format!("type is not a structure or enumeration")) + .emit(); + } + Err(CopyImplementationError::HasDestructor) => { + struct_span_err!(tcx.sess, + span, + E0184, + "the trait `Copy` may not be implemented for this type; the \ + type has a destructor") + .span_label(span, &format!("Copy not allowed on types with destructors")) + .emit(); + } + } +} + +fn visit_implementation_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + coerce_unsized_trait: DefId, + impl_did: DefId) { + debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", + impl_did); + + let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) { + Ok(id) => id, + Err(err) => { + tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err)); + } + }; + + let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) { + n + } else { + debug!("visit_implementation_of_coerce_unsized(): impl not \ + in this crate"); + return; + }; + + let source = tcx.item_type(impl_did); + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); + let target = trait_ref.substs.type_at(1); + debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", + source, + target); + + let span = tcx.map.span(impl_node_id); + let param_env = ParameterEnvironment::for_item(tcx, impl_node_id); + let source = source.subst(tcx, ¶m_env.free_substs); + let target = target.subst(tcx, ¶m_env.free_substs); + assert!(!source.has_escaping_regions()); + + debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", + source, + target); + + tcx.infer_ctxt(param_env, Reveal::ExactMatch).enter(|infcx| { + let cause = ObligationCause::misc(span, impl_node_id); + let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, + mt_b: ty::TypeAndMut<'tcx>, + mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| { + if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) { + infcx.report_mismatched_types(&cause, + mk_ptr(mt_b.ty), + target, + ty::error::TypeError::Mutability) + .emit(); + } + (mt_a.ty, mt_b.ty, unsize_trait, None) + }; + let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) { + (&ty::TyBox(a), &ty::TyBox(b)) => (a, b, unsize_trait, None), + + (&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => { + infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); + check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty)) + } + + (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) | + (&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => { + check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) + } + + (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) if def_a.is_struct() && + def_b.is_struct() => { + if def_a != def_b { + let source_path = tcx.item_path_str(def_a.did); + let target_path = tcx.item_path_str(def_b.did); + span_err!(tcx.sess, + span, + E0377, + "the trait `CoerceUnsized` may only be implemented \ + for a coercion between structures with the same \ + definition; expected {}, found {}", + source_path, + target_path); + return; + } + + let fields = &def_a.struct_variant().fields; + let diff_fields = fields.iter() + .enumerate() + .filter_map(|(i, f)| { + let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b)); + + if tcx.item_type(f.did).is_phantom_data() { + // Ignore PhantomData fields + return None; + } + + // Ignore fields that aren't significantly changed + if let Ok(ok) = infcx.sub_types(false, &cause, b, a) { + if ok.obligations.is_empty() { + return None; + } + } + + // Collect up all fields that were significantly changed + // i.e. those that contain T in coerce_unsized T -> U + Some((i, a, b)) + }) + .collect::>(); + + if diff_fields.is_empty() { + span_err!(tcx.sess, + span, + E0374, + "the trait `CoerceUnsized` may only be implemented \ + for a coercion between structures with one field \ + being coerced, none found"); + return; + } else if diff_fields.len() > 1 { + let item = tcx.map.expect_item(impl_node_id); + let span = if let ItemImpl(.., Some(ref t), _, _) = item.node { + t.path.span + } else { + tcx.map.span(impl_node_id) + }; + + let mut err = struct_span_err!(tcx.sess, + span, + E0375, + "implementing the trait \ + `CoerceUnsized` requires multiple \ + coercions"); + err.note("`CoerceUnsized` may only be implemented for \ + a coercion between structures with one field being coerced"); + err.note(&format!("currently, {} fields need coercions: {}", + diff_fields.len(), + diff_fields.iter() + .map(|&(i, a, b)| { + format!("{} ({} to {})", fields[i].name, a, b) + }) + .collect::>() + .join(", "))); + err.span_label(span, &format!("requires multiple coercions")); + err.emit(); + return; + } + + let (i, a, b) = diff_fields[0]; + let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); + (a, b, coerce_unsized_trait, Some(kind)) + } + + _ => { + span_err!(tcx.sess, + span, + E0376, + "the trait `CoerceUnsized` may only be implemented \ + for a coercion between structures"); + return; + } + }; + + let mut fulfill_cx = traits::FulfillmentContext::new(); + + // Register an obligation for `A: Trait`. + let cause = traits::ObligationCause::misc(span, impl_node_id); + let predicate = tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + + // Check that all transitive obligations are satisfied. + if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { + infcx.report_fulfillment_errors(&errors); + } + + // Finally, resolve all regions. + let mut free_regions = FreeRegionMap::new(); + free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment + .caller_bounds); + infcx.resolve_regions_and_report_errors(&free_regions, impl_node_id); + + if let Some(kind) = kind { + tcx.custom_coerce_unsized_kinds.borrow_mut().insert(impl_did, kind); + } + }); +} diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 3bbe5aa1fef3..6cf752dd69fc 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -16,45 +16,33 @@ // mappings. That mapping code resides here. use hir::def_id::DefId; -use middle::lang_items::UnsizeTraitLangItem; -use rustc::ty::subst::Subst; use rustc::ty::{self, TyCtxt, TypeFoldable}; -use rustc::traits::{self, ObligationCause, Reveal}; -use rustc::ty::ParameterEnvironment; use rustc::ty::{Ty, TyBool, TyChar, TyError}; use rustc::ty::{TyParam, TyRawPtr}; use rustc::ty::{TyRef, TyAdt, TyDynamic, TyNever, TyTuple}; use rustc::ty::{TyStr, TyArray, TySlice, TyFloat, TyInfer, TyInt}; use rustc::ty::{TyUint, TyClosure, TyBox, TyFnDef, TyFnPtr}; use rustc::ty::{TyProjection, TyAnon}; -use rustc::ty::util::CopyImplementationError; -use middle::free_region::FreeRegionMap; use CrateCtxt; -use rustc::infer::{self, InferCtxt}; use syntax_pos::Span; use rustc::dep_graph::DepNode; -use rustc::hir::map as hir_map; use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::{Item, ItemImpl}; use rustc::hir; +mod builtin; mod orphan; mod overlap; mod unsafety; -struct CoherenceChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - crate_context: &'a CrateCtxt<'a, 'gcx>, - inference_context: InferCtxt<'a, 'gcx, 'tcx>, +struct CoherenceChecker<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, } -struct CoherenceCheckVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - cc: &'a CoherenceChecker<'a, 'gcx, 'tcx>, -} - -impl<'a, 'gcx, 'tcx, 'v> ItemLikeVisitor<'v> for CoherenceCheckVisitor<'a, 'gcx, 'tcx> { +impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for CoherenceChecker<'a, 'tcx> { fn visit_item(&mut self, item: &Item) { if let ItemImpl(..) = item.node { - self.cc.check_implementation(item) + self.check_implementation(item) } } @@ -65,7 +53,7 @@ impl<'a, 'gcx, 'tcx, 'v> ItemLikeVisitor<'v> for CoherenceCheckVisitor<'a, 'gcx, } } -impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> { +impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { // Returns the def ID of the base type, if there is one. fn get_base_type_def_id(&self, span: Span, ty: Ty<'tcx>) -> Option { match ty.sty { @@ -73,7 +61,7 @@ impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> { TyDynamic(ref t, ..) => t.principal().map(|p| p.def_id()), - TyBox(_) => self.inference_context.tcx.lang_items.owned_box(), + TyBox(_) => self.tcx.lang_items.owned_box(), TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) | TyStr | TyArray(..) | TySlice(..) | TyFnDef(..) | TyFnPtr(_) | TyTuple(..) | TyParam(..) | TyError | @@ -89,36 +77,22 @@ impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> { } } - fn check(&self) { + fn check(&mut self) { // Check implementations and traits. This populates the tables // containing the inherent methods and extension methods. It also // builds up the trait inheritance table. - self.crate_context.tcx.visit_all_item_likes_in_krate( - DepNode::CoherenceCheckImpl, - &mut CoherenceCheckVisitor { cc: self }); - - // Populate the table of destructors. It might seem a bit strange to - // do this here, but it's actually the most convenient place, since - // the coherence tables contain the trait -> type mappings. - self.populate_destructors(); - - // Check to make sure implementations of `Copy` are legal. - self.check_implementations_of_copy(); - - // Check to make sure implementations of `CoerceUnsized` are legal - // and collect the necessary information from them. - self.check_implementations_of_coerce_unsized(); + self.tcx.visit_all_item_likes_in_krate(DepNode::CoherenceCheckImpl, self); } fn check_implementation(&self, item: &Item) { - let tcx = self.crate_context.tcx; + let tcx = self.tcx; let impl_did = tcx.map.local_def_id(item.id); let self_type = tcx.item_type(impl_did); // If there are no traits, then this implementation must have a // base type. - if let Some(trait_ref) = self.crate_context.tcx.impl_trait_ref(impl_did) { + if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { debug!("(checking implementation) adding impl for trait '{:?}', item '{}'", trait_ref, item.name); @@ -129,9 +103,7 @@ impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> { return; } - enforce_trait_manually_implementable(self.crate_context.tcx, - item.span, - trait_ref.def_id); + enforce_trait_manually_implementable(self.tcx, item.span, trait_ref.def_id); self.add_trait_impl(trait_ref, impl_did); } else { // Skip inherent impls where the self type is an error @@ -150,348 +122,15 @@ impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> { } fn add_inherent_impl(&self, base_def_id: DefId, impl_def_id: DefId) { - let tcx = self.crate_context.tcx; - tcx.inherent_impls.borrow_mut().push(base_def_id, impl_def_id); + self.tcx.inherent_impls.borrow_mut().push(base_def_id, impl_def_id); } - fn add_trait_impl(&self, impl_trait_ref: ty::TraitRef<'gcx>, impl_def_id: DefId) { + fn add_trait_impl(&self, impl_trait_ref: ty::TraitRef<'tcx>, impl_def_id: DefId) { debug!("add_trait_impl: impl_trait_ref={:?} impl_def_id={:?}", impl_trait_ref, impl_def_id); - let trait_def = self.crate_context.tcx.lookup_trait_def(impl_trait_ref.def_id); - trait_def.record_local_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref); - } - - // Destructors - // - - fn populate_destructors(&self) { - let tcx = self.crate_context.tcx; - let drop_trait = match tcx.lang_items.drop_trait() { - Some(id) => id, - None => return, - }; - tcx.populate_implementations_for_trait_if_necessary(drop_trait); - let drop_trait = tcx.lookup_trait_def(drop_trait); - - drop_trait.for_each_impl(tcx, |impl_did| { - let items = tcx.associated_item_def_ids(impl_did); - if items.is_empty() { - // We'll error out later. For now, just don't ICE. - return; - } - let method_def_id = items[0]; - - let self_type = tcx.item_type(impl_did); - match self_type.sty { - ty::TyAdt(type_def, _) => { - type_def.set_destructor(method_def_id); - } - _ => { - // Destructors only work on nominal types. - if let Some(impl_node_id) = tcx.map.as_local_node_id(impl_did) { - match tcx.map.find(impl_node_id) { - Some(hir_map::NodeItem(item)) => { - let span = match item.node { - ItemImpl(.., ref ty, _) => ty.span, - _ => item.span, - }; - struct_span_err!(tcx.sess, - span, - E0120, - "the Drop trait may only be implemented on \ - structures") - .span_label(span, - &format!("implementing Drop requires a struct")) - .emit(); - } - _ => { - bug!("didn't find impl in ast map"); - } - } - } else { - bug!("found external impl of Drop trait on \ - something other than a struct"); - } - } - } - }); - } - - /// Ensures that implementations of the built-in trait `Copy` are legal. - fn check_implementations_of_copy(&self) { - let tcx = self.crate_context.tcx; - let copy_trait = match tcx.lang_items.copy_trait() { - Some(id) => id, - None => return, - }; - tcx.populate_implementations_for_trait_if_necessary(copy_trait); - let copy_trait = tcx.lookup_trait_def(copy_trait); - - copy_trait.for_each_impl(tcx, |impl_did| { - debug!("check_implementations_of_copy: impl_did={:?}", impl_did); - - let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) { - n - } else { - debug!("check_implementations_of_copy(): impl not in this \ - crate"); - return; - }; - - let self_type = tcx.item_type(impl_did); - debug!("check_implementations_of_copy: self_type={:?} (bound)", - self_type); - - let span = tcx.map.span(impl_node_id); - let param_env = ParameterEnvironment::for_item(tcx, impl_node_id); - let self_type = self_type.subst(tcx, ¶m_env.free_substs); - assert!(!self_type.has_escaping_regions()); - - debug!("check_implementations_of_copy: self_type={:?} (free)", - self_type); - - match param_env.can_type_implement_copy(tcx, self_type, span) { - Ok(()) => {} - Err(CopyImplementationError::InfrigingField(name)) => { - struct_span_err!(tcx.sess, - span, - E0204, - "the trait `Copy` may not be implemented for this type") - .span_label(span, &format!("field `{}` does not implement `Copy`", name)) - .emit() - } - Err(CopyImplementationError::InfrigingVariant(name)) => { - let item = tcx.map.expect_item(impl_node_id); - let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node { - tr.path.span - } else { - span - }; - - struct_span_err!(tcx.sess, - span, - E0205, - "the trait `Copy` may not be implemented for this type") - .span_label(span, - &format!("variant `{}` does not implement `Copy`", name)) - .emit() - } - Err(CopyImplementationError::NotAnAdt) => { - let item = tcx.map.expect_item(impl_node_id); - let span = if let ItemImpl(.., ref ty, _) = item.node { - ty.span - } else { - span - }; - - struct_span_err!(tcx.sess, - span, - E0206, - "the trait `Copy` may not be implemented for this type") - .span_label(span, &format!("type is not a structure or enumeration")) - .emit(); - } - Err(CopyImplementationError::HasDestructor) => { - struct_span_err!(tcx.sess, - span, - E0184, - "the trait `Copy` may not be implemented for this type; the \ - type has a destructor") - .span_label(span, &format!("Copy not allowed on types with destructors")) - .emit(); - } - } - }); - } - - /// Process implementations of the built-in trait `CoerceUnsized`. - fn check_implementations_of_coerce_unsized(&self) { - let tcx = self.crate_context.tcx; - let coerce_unsized_trait = match tcx.lang_items.coerce_unsized_trait() { - Some(id) => id, - None => return, - }; - let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) { - Ok(id) => id, - Err(err) => { - tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err)); - } - }; - - let trait_def = tcx.lookup_trait_def(coerce_unsized_trait); - - trait_def.for_each_impl(tcx, |impl_did| { - debug!("check_implementations_of_coerce_unsized: impl_did={:?}", - impl_did); - - let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) { - n - } else { - debug!("check_implementations_of_coerce_unsized(): impl not \ - in this crate"); - return; - }; - - let source = tcx.item_type(impl_did); - let trait_ref = self.crate_context.tcx.impl_trait_ref(impl_did).unwrap(); - let target = trait_ref.substs.type_at(1); - debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (bound)", - source, - target); - - let span = tcx.map.span(impl_node_id); - let param_env = ParameterEnvironment::for_item(tcx, impl_node_id); - let source = source.subst(tcx, ¶m_env.free_substs); - let target = target.subst(tcx, ¶m_env.free_substs); - assert!(!source.has_escaping_regions()); - - debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)", - source, - target); - - tcx.infer_ctxt(None, Some(param_env), Reveal::ExactMatch).enter(|infcx| { - let cause = ObligationCause::misc(span, impl_node_id); - let check_mutbl = |mt_a: ty::TypeAndMut<'gcx>, - mt_b: ty::TypeAndMut<'gcx>, - mk_ptr: &Fn(Ty<'gcx>) -> Ty<'gcx>| { - if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) { - infcx.report_mismatched_types(&cause, - mk_ptr(mt_b.ty), - target, - ty::error::TypeError::Mutability).emit(); - } - (mt_a.ty, mt_b.ty, unsize_trait, None) - }; - let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) { - (&ty::TyBox(a), &ty::TyBox(b)) => (a, b, unsize_trait, None), - - (&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => { - infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); - check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty)) - } - - (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) | - (&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => { - check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) - } - - (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) - if def_a.is_struct() && def_b.is_struct() => { - if def_a != def_b { - let source_path = tcx.item_path_str(def_a.did); - let target_path = tcx.item_path_str(def_b.did); - span_err!(tcx.sess, - span, - E0377, - "the trait `CoerceUnsized` may only be implemented \ - for a coercion between structures with the same \ - definition; expected {}, found {}", - source_path, - target_path); - return; - } - - let fields = &def_a.struct_variant().fields; - let diff_fields = fields.iter() - .enumerate() - .filter_map(|(i, f)| { - let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b)); - - if tcx.item_type(f.did).is_phantom_data() { - // Ignore PhantomData fields - return None; - } - - // Ignore fields that aren't significantly changed - if let Ok(ok) = infcx.sub_types(false, &cause, b, a) { - if ok.obligations.is_empty() { - return None; - } - } - - // Collect up all fields that were significantly changed - // i.e. those that contain T in coerce_unsized T -> U - Some((i, a, b)) - }) - .collect::>(); - - if diff_fields.is_empty() { - span_err!(tcx.sess, - span, - E0374, - "the trait `CoerceUnsized` may only be implemented \ - for a coercion between structures with one field \ - being coerced, none found"); - return; - } else if diff_fields.len() > 1 { - let item = tcx.map.expect_item(impl_node_id); - let span = if let ItemImpl(.., Some(ref t), _, _) = item.node { - t.path.span - } else { - tcx.map.span(impl_node_id) - }; - - let mut err = struct_span_err!(tcx.sess, - span, - E0375, - "implementing the trait \ - `CoerceUnsized` requires multiple \ - coercions"); - err.note("`CoerceUnsized` may only be implemented for \ - a coercion between structures with one field being coerced"); - err.note(&format!("currently, {} fields need coercions: {}", - diff_fields.len(), - diff_fields.iter() - .map(|&(i, a, b)| { - format!("{} ({} to {})", fields[i].name, a, b) - }) - .collect::>() - .join(", "))); - err.span_label(span, &format!("requires multiple coercions")); - err.emit(); - return; - } - - let (i, a, b) = diff_fields[0]; - let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); - (a, b, coerce_unsized_trait, Some(kind)) - } - - _ => { - span_err!(tcx.sess, - span, - E0376, - "the trait `CoerceUnsized` may only be implemented \ - for a coercion between structures"); - return; - } - }; - - let mut fulfill_cx = traits::FulfillmentContext::new(); - - // Register an obligation for `A: Trait`. - let cause = traits::ObligationCause::misc(span, impl_node_id); - let predicate = - tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]); - fulfill_cx.register_predicate_obligation(&infcx, predicate); - - // Check that all transitive obligations are satisfied. - if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(&errors); - } - - // Finally, resolve all regions. - let mut free_regions = FreeRegionMap::new(); - free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment - .caller_bounds); - infcx.resolve_regions_and_report_errors(&free_regions, impl_node_id); - - if let Some(kind) = kind { - tcx.custom_coerce_unsized_kinds.borrow_mut().insert(impl_did, kind); - } - }); - }); + let trait_def = self.tcx.lookup_trait_def(impl_trait_ref.def_id); + trait_def.record_local_impl(self.tcx, impl_def_id, impl_trait_ref); } } @@ -524,14 +163,9 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt, sp: Span, trait_def_id: Def pub fn check_coherence(ccx: &CrateCtxt) { let _task = ccx.tcx.dep_graph.in_task(DepNode::Coherence); - ccx.tcx.infer_ctxt(None, None, Reveal::ExactMatch).enter(|infcx| { - CoherenceChecker { - crate_context: ccx, - inference_context: infcx, - } - .check(); - }); + CoherenceChecker { tcx: ccx.tcx }.check(); unsafety::check(ccx.tcx); orphan::check(ccx.tcx); overlap::check(ccx.tcx); + builtin::check(ccx.tcx); } diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index a09bdf0533a1..08293c1d874a 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -87,7 +87,7 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { for (i, &impl1_def_id) in impls.iter().enumerate() { for &impl2_def_id in &impls[(i + 1)..] { - self.tcx.infer_ctxt(None, None, Reveal::ExactMatch).enter(|infcx| { + self.tcx.infer_ctxt((), Reveal::ExactMatch).enter(|infcx| { if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() { self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id) } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7ddf10375bf2..f832bf8d86e9 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -63,7 +63,7 @@ use constrained_type_params as ctp; use middle::lang_items::SizedTraitLangItem; use middle::const_val::ConstVal; use rustc_const_eval::EvalHint::UncheckedExprHint; -use rustc_const_eval::{eval_const_expr_partial, report_const_eval_err}; +use rustc_const_eval::{ConstContext, report_const_eval_err}; use rustc::ty::subst::Substs; use rustc::ty::{ToPredicate, ImplContainer, AssociatedItemContainer, TraitContainer}; use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; @@ -1039,8 +1039,9 @@ fn convert_union_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, adt } - fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, e: &hir::Expr) + fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId) -> Option { + let e = &ccx.tcx.map.body(body).value; debug!("disr expr, checking {}", ccx.tcx.map.node_to_pretty_string(e.id)); let ty_hint = repr_ty.to_ty(ccx.tcx); @@ -1052,9 +1053,9 @@ fn convert_union_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, }; let hint = UncheckedExprHint(ty_hint); - match eval_const_expr_partial(ccx.tcx, e, hint, None) { + match ConstContext::new(ccx.tcx, body).eval(e, hint) { Ok(ConstVal::Integral(i)) => { - // FIXME: eval_const_expr_partial should return an error if the hint is wrong + // FIXME: eval should return an error if the hint is wrong match (repr_ty, i) { (attr::SignedInt(ast::IntTy::I8), ConstInt::I8(_)) | (attr::SignedInt(ast::IntTy::I16), ConstInt::I16(_)) | @@ -1103,7 +1104,6 @@ fn convert_enum_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let variants = def.variants.iter().map(|v| { let wrapped_disr = prev_disr.map_or(initial, |d| d.wrap_incr()); let disr = if let Some(e) = v.node.disr_expr { - let e = &tcx.map.body(e).value; evaluate_disr_expr(ccx, repr_type, e) } else if let Some(disr) = repr_type.disr_incr(tcx, prev_disr) { Some(disr) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index d3b671f2a4d6..1a971be64d81 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -2300,6 +2300,7 @@ This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (this differs from the behavior for `&T`, which is always `Copy`). "##, +/* E0205: r##" An attempt to implement the `Copy` trait for an enum failed because one of the variants does not implement `Copy`. To fix this, you must implement `Copy` for @@ -2329,6 +2330,7 @@ enum Foo<'a> { This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (this differs from the behavior for `&T`, which is always `Copy`). "##, +*/ E0206: r##" You can only implement `Copy` for a struct or enum. Both of the following diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index ec17813ed2a5..6d30a85b5829 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -70,7 +70,7 @@ This API is completely unstable and subject to change. #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(not(stage0), deny(warnings))] +#![deny(warnings)] #![allow(non_camel_case_types)] @@ -177,7 +177,7 @@ fn require_same_types<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, expected: Ty<'tcx>, actual: Ty<'tcx>) -> bool { - ccx.tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| { + ccx.tcx.infer_ctxt((), Reveal::NotSpecializable).enter(|infcx| { match infcx.eq_types(false, &cause, expected, actual) { Ok(InferOk { obligations, .. }) => { // FIXME(#32730) propagate obligations diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index d66d2001f230..93c0bd6d6d83 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -14,7 +14,6 @@ arena = { path = "../libarena" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } rustc_const_eval = { path = "../librustc_const_eval" } -rustc_const_math = { path = "../librustc_const_math" } rustc_driver = { path = "../librustc_driver" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index e0a26ca31818..6a640e7b5ed8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1692,22 +1692,10 @@ impl Clean for hir::Ty { BorrowedRef {lifetime: l.clean(cx), mutability: m.mutbl.clean(cx), type_: box m.ty.clean(cx)}, TySlice(ref ty) => Vector(box ty.clean(cx)), - TyArray(ref ty, e) => { - use rustc_const_math::{ConstInt, ConstUsize}; - use rustc_const_eval::eval_const_expr; - use rustc::middle::const_val::ConstVal; - - let e = &cx.tcx.map.body(e).value; - let n = match eval_const_expr(cx.tcx, e) { - ConstVal::Integral(ConstInt::Usize(u)) => match u { - ConstUsize::Us16(u) => u.to_string(), - ConstUsize::Us32(u) => u.to_string(), - ConstUsize::Us64(u) => u.to_string(), - }, - // after type checking this can't fail - _ => unreachable!(), - }; - FixedVector(box ty.clean(cx), n) + TyArray(ref ty, length) => { + use rustc_const_eval::eval_length; + let n = eval_length(cx.tcx, length, "array length").unwrap(); + FixedVector(box ty.clean(cx), n.to_string()) }, TyTup(ref tys) => Tuple(tys.clean(cx)), TyPath(hir::QPath::Resolved(None, ref path)) => { @@ -2219,8 +2207,8 @@ impl Path { } } - pub fn last_name(&self) -> String { - self.segments.last().unwrap().name.clone() + pub fn last_name(&self) -> &str { + self.segments.last().unwrap().name.as_str() } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 1f989c557d3a..514eb6ea843d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -156,9 +156,15 @@ pub fn run_core(search_paths: SearchPaths, let name = link::find_crate_name(Some(&sess), &krate.attrs, &input); let driver::ExpansionResult { defs, analysis, resolutions, mut hir_forest, .. } = { - driver::phase_2_configure_and_expand( - &sess, &cstore, krate, None, &name, None, resolve::MakeGlobMap::No, |_| Ok(()), - ).expect("phase_2_configure_and_expand aborted in rustdoc!") + let result = driver::phase_2_configure_and_expand(&sess, + &cstore, + krate, + None, + &name, + None, + resolve::MakeGlobMap::No, + |_| Ok(())); + abort_on_err(result, &sess) }; let arena = DroplessArena::new(); diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 796a3a93068c..716ad05401d0 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -433,7 +433,7 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec)> { /// Used when rendering a `ResolvedPath` structure. This invokes the `path` /// rendering function with the necessary arguments for linking to a local path. fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path, - print_all: bool) -> fmt::Result { + print_all: bool, use_absolute: bool) -> fmt::Result { let last = path.segments.last().unwrap(); let rel_root = match &*path.segments[0].name { "self" => Some("./".to_string()), @@ -467,7 +467,17 @@ fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path, if w.alternate() { write!(w, "{:#}{:#}", HRef::new(did, &last.name), last.params)?; } else { - write!(w, "{}{}", HRef::new(did, &last.name), last.params)?; + let path = if use_absolute { + match href(did) { + Some((_, _, fqp)) => format!("{}::{}", + fqp[..fqp.len()-1].join("::"), + HRef::new(did, fqp.last().unwrap())), + None => format!("{}", HRef::new(did, &last.name)), + } + } else { + format!("{}", HRef::new(did, &last.name)) + }; + write!(w, "{}{}", path, last.params)?; } Ok(()) } @@ -551,194 +561,201 @@ impl<'a> fmt::Display for HRef<'a> { } } -impl fmt::Display for clean::Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - clean::Generic(ref name) => { - f.write_str(name) +fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt::Result { + match *t { + clean::Generic(ref name) => { + f.write_str(name) + } + clean::ResolvedPath{ did, ref typarams, ref path, is_generic } => { + // Paths like T::Output and Self::Output should be rendered with all segments + resolved_path(f, did, path, is_generic, use_absolute)?; + tybounds(f, typarams) + } + clean::Infer => write!(f, "_"), + clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()), + clean::BareFunction(ref decl) => { + if f.alternate() { + write!(f, "{}{}fn{:#}{:#}", + UnsafetySpace(decl.unsafety), + AbiSpace(decl.abi), + decl.generics, + decl.decl) + } else { + write!(f, "{}{}fn{}{}", + UnsafetySpace(decl.unsafety), + AbiSpace(decl.abi), + decl.generics, + decl.decl) } - clean::ResolvedPath{ did, ref typarams, ref path, is_generic } => { - // Paths like T::Output and Self::Output should be rendered with all segments - resolved_path(f, did, path, is_generic)?; - tybounds(f, typarams) - } - clean::Infer => write!(f, "_"), - clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()), - clean::BareFunction(ref decl) => { - if f.alternate() { - write!(f, "{}{}fn{:#}{:#}", - UnsafetySpace(decl.unsafety), - AbiSpace(decl.abi), - decl.generics, - decl.decl) - } else { - write!(f, "{}{}fn{}{}", - UnsafetySpace(decl.unsafety), - AbiSpace(decl.abi), - decl.generics, - decl.decl) + } + clean::Tuple(ref typs) => { + match &typs[..] { + &[] => primitive_link(f, PrimitiveType::Tuple, "()"), + &[ref one] => { + primitive_link(f, PrimitiveType::Tuple, "(")?; + //carry f.alternate() into this display w/o branching manually + fmt::Display::fmt(one, f)?; + primitive_link(f, PrimitiveType::Tuple, ",)") + } + many => { + primitive_link(f, PrimitiveType::Tuple, "(")?; + fmt::Display::fmt(&CommaSep(&many), f)?; + primitive_link(f, PrimitiveType::Tuple, ")") } } - clean::Tuple(ref typs) => { - match &typs[..] { - &[] => primitive_link(f, PrimitiveType::Tuple, "()"), - &[ref one] => { - primitive_link(f, PrimitiveType::Tuple, "(")?; - //carry f.alternate() into this display w/o branching manually - fmt::Display::fmt(one, f)?; - primitive_link(f, PrimitiveType::Tuple, ",)") - } - many => { - primitive_link(f, PrimitiveType::Tuple, "(")?; - fmt::Display::fmt(&CommaSep(&many), f)?; - primitive_link(f, PrimitiveType::Tuple, ")") - } - } + } + clean::Vector(ref t) => { + primitive_link(f, PrimitiveType::Slice, &format!("["))?; + fmt::Display::fmt(t, f)?; + primitive_link(f, PrimitiveType::Slice, &format!("]")) + } + clean::FixedVector(ref t, ref s) => { + primitive_link(f, PrimitiveType::Array, "[")?; + fmt::Display::fmt(t, f)?; + if f.alternate() { + primitive_link(f, PrimitiveType::Array, + &format!("; {}]", s)) + } else { + primitive_link(f, PrimitiveType::Array, + &format!("; {}]", Escape(s))) } - clean::Vector(ref t) => { - primitive_link(f, PrimitiveType::Slice, &format!("["))?; - fmt::Display::fmt(t, f)?; - primitive_link(f, PrimitiveType::Slice, &format!("]")) - } - clean::FixedVector(ref t, ref s) => { - primitive_link(f, PrimitiveType::Array, "[")?; - fmt::Display::fmt(t, f)?; - if f.alternate() { - primitive_link(f, PrimitiveType::Array, - &format!("; {}]", s)) - } else { - primitive_link(f, PrimitiveType::Array, - &format!("; {}]", Escape(s))) - } - } - clean::Never => f.write_str("!"), - clean::RawPointer(m, ref t) => { - match **t { - clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => { - if f.alternate() { - primitive_link(f, clean::PrimitiveType::RawPointer, - &format!("*{}{:#}", RawMutableSpace(m), t)) - } else { - primitive_link(f, clean::PrimitiveType::RawPointer, - &format!("*{}{}", RawMutableSpace(m), t)) - } - } - _ => { + } + clean::Never => f.write_str("!"), + clean::RawPointer(m, ref t) => { + match **t { + clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => { + if f.alternate() { primitive_link(f, clean::PrimitiveType::RawPointer, - &format!("*{}", RawMutableSpace(m)))?; - fmt::Display::fmt(t, f) + &format!("*{}{:#}", RawMutableSpace(m), t)) + } else { + primitive_link(f, clean::PrimitiveType::RawPointer, + &format!("*{}{}", RawMutableSpace(m), t)) } } + _ => { + primitive_link(f, clean::PrimitiveType::RawPointer, + &format!("*{}", RawMutableSpace(m)))?; + fmt::Display::fmt(t, f) + } } - clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => { - let lt = match *l { - Some(ref l) => format!("{} ", *l), - _ => "".to_string(), - }; - let m = MutableSpace(mutability); - match **ty { - clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T] - match **bt { - clean::Generic(_) => - if f.alternate() { - primitive_link(f, PrimitiveType::Slice, - &format!("&{}{}[{:#}]", lt, m, **bt)) - } else { - primitive_link(f, PrimitiveType::Slice, - &format!("&{}{}[{}]", lt, m, **bt)) - }, - _ => { - if f.alternate() { - primitive_link(f, PrimitiveType::Slice, - &format!("&{}{}[", lt, m))?; - write!(f, "{:#}", **bt)?; - } else { - primitive_link(f, PrimitiveType::Slice, - &format!("&{}{}[", lt, m))?; - write!(f, "{}", **bt)?; - } - primitive_link(f, PrimitiveType::Slice, "]") + } + clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => { + let lt = match *l { + Some(ref l) => format!("{} ", *l), + _ => "".to_string(), + }; + let m = MutableSpace(mutability); + match **ty { + clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T] + match **bt { + clean::Generic(_) => + if f.alternate() { + primitive_link(f, PrimitiveType::Slice, + &format!("&{}{}[{:#}]", lt, m, **bt)) + } else { + primitive_link(f, PrimitiveType::Slice, + &format!("&{}{}[{}]", lt, m, **bt)) + }, + _ => { + if f.alternate() { + primitive_link(f, PrimitiveType::Slice, + &format!("&{}{}[", lt, m))?; + write!(f, "{:#}", **bt)?; + } else { + primitive_link(f, PrimitiveType::Slice, + &format!("&{}{}[", lt, m))?; + write!(f, "{}", **bt)?; } - } - } - _ => { - if f.alternate() { - write!(f, "&{}{}{:#}", lt, m, **ty) - } else { - write!(f, "&{}{}{}", lt, m, **ty) + primitive_link(f, PrimitiveType::Slice, "]") } } } - } - clean::PolyTraitRef(ref bounds) => { - for (i, bound) in bounds.iter().enumerate() { - if i != 0 { - write!(f, " + ")?; - } + _ => { if f.alternate() { - write!(f, "{:#}", *bound)?; + write!(f, "&{}{}{:#}", lt, m, **ty) } else { - write!(f, "{}", *bound)?; + write!(f, "&{}{}{}", lt, m, **ty) } } - Ok(()) } - clean::ImplTrait(ref bounds) => { - write!(f, "impl ")?; - for (i, bound) in bounds.iter().enumerate() { - if i != 0 { - write!(f, " + ")?; - } - if f.alternate() { - write!(f, "{:#}", *bound)?; - } else { - write!(f, "{}", *bound)?; - } + } + clean::PolyTraitRef(ref bounds) => { + for (i, bound) in bounds.iter().enumerate() { + if i != 0 { + write!(f, " + ")?; } - Ok(()) - } - // It's pretty unsightly to look at `::C` in output, and - // we've got hyperlinking on our side, so try to avoid longer - // notation as much as possible by making `C` a hyperlink to trait - // `B` to disambiguate. - // - // FIXME: this is still a lossy conversion and there should probably - // be a better way of representing this in general? Most of - // the ugliness comes from inlining across crates where - // everything comes in as a fully resolved QPath (hard to - // look at). - clean::QPath { - ref name, - ref self_type, - trait_: box clean::ResolvedPath { did, ref typarams, .. }, - } => { if f.alternate() { - write!(f, "{:#}::", self_type)?; + write!(f, "{:#}", *bound)?; } else { - write!(f, "{}::", self_type)?; + write!(f, "{}", *bound)?; } - let path = clean::Path::singleton(name.clone()); - resolved_path(f, did, &path, false)?; + } + Ok(()) + } + clean::ImplTrait(ref bounds) => { + write!(f, "impl ")?; + for (i, bound) in bounds.iter().enumerate() { + if i != 0 { + write!(f, " + ")?; + } + if f.alternate() { + write!(f, "{:#}", *bound)?; + } else { + write!(f, "{}", *bound)?; + } + } + Ok(()) + } + // It's pretty unsightly to look at `::C` in output, and + // we've got hyperlinking on our side, so try to avoid longer + // notation as much as possible by making `C` a hyperlink to trait + // `B` to disambiguate. + // + // FIXME: this is still a lossy conversion and there should probably + // be a better way of representing this in general? Most of + // the ugliness comes from inlining across crates where + // everything comes in as a fully resolved QPath (hard to + // look at). + clean::QPath { + ref name, + ref self_type, + trait_: box clean::ResolvedPath { did, ref typarams, .. }, + } => { + if f.alternate() { + write!(f, "{:#}::", self_type)?; + } else { + write!(f, "{}::", self_type)?; + } + let path = clean::Path::singleton(name.clone()); + resolved_path(f, did, &path, true, use_absolute)?; - // FIXME: `typarams` are not rendered, and this seems bad? - drop(typarams); - Ok(()) - } - clean::QPath { ref name, ref self_type, ref trait_ } => { - if f.alternate() { - write!(f, "<{:#} as {:#}>::{}", self_type, trait_, name) - } else { - write!(f, "<{} as {}>::{}", self_type, trait_, name) - } - } - clean::Unique(..) => { - panic!("should have been cleaned") + // FIXME: `typarams` are not rendered, and this seems bad? + drop(typarams); + Ok(()) + } + clean::QPath { ref name, ref self_type, ref trait_ } => { + if f.alternate() { + write!(f, "<{:#} as {:#}>::{}", self_type, trait_, name) + } else { + write!(f, "<{} as {}>::{}", self_type, trait_, name) } } + clean::Unique(..) => { + panic!("should have been cleaned") + } } } -fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::Result { +impl fmt::Display for clean::Type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt_type(self, f, false) + } +} + +fn fmt_impl(i: &clean::Impl, + f: &mut fmt::Formatter, + link_trait: bool, + use_absolute: bool) -> fmt::Result { let mut plain = String::new(); if f.alternate() { @@ -759,7 +776,7 @@ fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::R plain.push_str(&format!("{:#}", ty)); } else { match *ty { - clean::ResolvedPath{ typarams: None, ref path, is_generic: false, .. } => { + clean::ResolvedPath { typarams: None, ref path, is_generic: false, .. } => { let last = path.segments.last().unwrap(); fmt::Display::fmt(&last.name, f)?; fmt::Display::fmt(&last.params, f)?; @@ -772,7 +789,7 @@ fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::R plain.push_str(" for "); } - fmt::Display::fmt(&i.for_, f)?; + fmt_type(&i.for_, f, use_absolute)?; plain.push_str(&format!("{:#}", i.for_)); fmt::Display::fmt(&WhereClause(&i.generics, plain.len() + 1), f)?; @@ -781,13 +798,15 @@ fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::R impl fmt::Display for clean::Impl { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt_impl(self, f, true) + fmt_impl(self, f, true, false) } } // The difference from above is that trait is not hyperlinked. -pub fn fmt_impl_for_trait_page(i: &clean::Impl, f: &mut fmt::Formatter) -> fmt::Result { - fmt_impl(i, f, false) +pub fn fmt_impl_for_trait_page(i: &clean::Impl, + f: &mut fmt::Formatter, + use_absolute: bool) -> fmt::Result { + fmt_impl(i, f, false, use_absolute) } impl fmt::Display for clean::Arguments { @@ -978,7 +997,7 @@ impl fmt::Display for clean::Import { impl fmt::Display for clean::ImportSource { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.did { - Some(did) => resolved_path(f, did, &self.path, true), + Some(did) => resolved_path(f, did, &self.path, true, false), _ => { for (i, seg) in self.path.segments.iter().enumerate() { if i > 0 { diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index bd47b1e7c121..a031be8b3c2b 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -218,9 +218,11 @@ impl<'a> Classifier<'a> { token::Comment => Class::Comment, token::DocComment(..) => Class::DocComment, - // If this '&' token is directly adjacent to another token, assume - // that it's the address-of operator instead of the and-operator. - token::BinOp(token::And) if self.lexer.peek().sp.lo == tas.sp.hi => Class::RefKeyWord, + // If this '&' or '*' token is followed by a non-whitespace token, assume that it's the + // reference or dereference operator or a reference or pointer type, instead of the + // bit-and or multiplication operator. + token::BinOp(token::And) | token::BinOp(token::Star) + if self.lexer.peek().tok != token::Whitespace => Class::RefKeyWord, // Consider this as part of a macro invocation if there was a // leading identifier. diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 67cf12f4f4a6..f2427008a7d4 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -49,6 +49,8 @@ pub struct Markdown<'a>(pub &'a str); /// A unit struct like `Markdown`, that renders the markdown with a /// table of contents. pub struct MarkdownWithToc<'a>(pub &'a str); +/// A unit struct like `Markdown`, that renders the markdown escaping HTML tags. +pub struct MarkdownHtml<'a>(pub &'a str); const DEF_OUNIT: libc::size_t = 64; const HOEDOWN_EXT_NO_INTRA_EMPHASIS: libc::c_uint = 1 << 11; @@ -58,6 +60,7 @@ const HOEDOWN_EXT_AUTOLINK: libc::c_uint = 1 << 3; const HOEDOWN_EXT_STRIKETHROUGH: libc::c_uint = 1 << 4; const HOEDOWN_EXT_SUPERSCRIPT: libc::c_uint = 1 << 8; const HOEDOWN_EXT_FOOTNOTES: libc::c_uint = 1 << 2; +const HOEDOWN_HTML_ESCAPE: libc::c_uint = 1 << 1; const HOEDOWN_EXTENSIONS: libc::c_uint = HOEDOWN_EXT_NO_INTRA_EMPHASIS | HOEDOWN_EXT_TABLES | @@ -220,7 +223,11 @@ thread_local!(pub static PLAYGROUND: RefCell, String)>> = RefCell::new(None) }); -pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { + +pub fn render(w: &mut fmt::Formatter, + s: &str, + print_toc: bool, + html_flags: libc::c_uint) -> fmt::Result { extern fn block(ob: *mut hoedown_buffer, orig_text: *const hoedown_buffer, lang: *const hoedown_buffer, data: *const hoedown_renderer_data) { unsafe { @@ -383,7 +390,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { unsafe { let ob = hoedown_buffer_new(DEF_OUNIT); - let renderer = hoedown_html_renderer_new(0, 0); + let renderer = hoedown_html_renderer_new(html_flags, 0); let mut opaque = MyOpaque { dfltblk: (*renderer).blockcode.unwrap(), toc_builder: if print_toc {Some(TocBuilder::new())} else {None} @@ -553,14 +560,23 @@ impl<'a> fmt::Display for Markdown<'a> { let Markdown(md) = *self; // This is actually common enough to special-case if md.is_empty() { return Ok(()) } - render(fmt, md, false) + render(fmt, md, false, 0) } } impl<'a> fmt::Display for MarkdownWithToc<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let MarkdownWithToc(md) = *self; - render(fmt, md, true) + render(fmt, md, true, 0) + } +} + +impl<'a> fmt::Display for MarkdownHtml<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let MarkdownHtml(md) = *self; + // This is actually common enough to special-case + if md.is_empty() { return Ok(()) } + render(fmt, md, false, HOEDOWN_HTML_ESCAPE) } } @@ -613,7 +629,7 @@ pub fn plain_summary_line(md: &str) -> String { #[cfg(test)] mod tests { - use super::{LangString, Markdown}; + use super::{LangString, Markdown, MarkdownHtml}; use super::plain_summary_line; use html::render::reset_ids; @@ -719,4 +735,15 @@ mod tests { t("# top header", "top header"); t("## header", "header"); } + + #[test] + fn test_markdown_html_escape() { + fn t(input: &str, expect: &str) { + let output = format!("{}", MarkdownHtml(input)); + assert_eq!(output, expect); + } + + t("`Struct<'a, T>`", "

Struct<'a, T>

\n"); + t("Struct<'a, T>", "

Struct<'a, T>

\n"); + } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index ff7133f5d0c9..e161f40b7295 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -71,7 +71,7 @@ use html::format::{TyParamBounds, WhereClause, href, AbiSpace}; use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace}; use html::format::fmt_impl_for_trait_page; use html::item_type::ItemType; -use html::markdown::{self, Markdown}; +use html::markdown::{self, Markdown, MarkdownHtml}; use html::{highlight, layout}; /// A pair of name and its optional document. @@ -1866,13 +1866,15 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec{}", text)) }; if stab.level == stability::Unstable { - let unstable_extra = if show_reason { - match (!stab.feature.is_empty(), &cx.shared.issue_tracker_base_url, stab.issue) { + if show_reason { + let unstable_extra = match (!stab.feature.is_empty(), + &cx.shared.issue_tracker_base_url, + stab.issue) { (true, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 => format!(" ({}
#{})", Escape(&stab.feature), tracker_url, issue_no, issue_no), @@ -1882,17 +1884,22 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec format!(" ({})", Escape(&stab.feature)), _ => String::new(), + }; + if stab.unstable_reason.is_empty() { + stability.push(format!("
\ + 🔬 \ + This is a nightly-only experimental API. {}
", + unstable_extra)); + } else { + let text = format!("🔬 \ + This is a nightly-only experimental API. {}{}", + unstable_extra, MarkdownHtml(&stab.unstable_reason)); + stability.push(format!("
{}
", + text)); } } else { - String::new() - }; - let unstable_reason = if show_reason && !stab.unstable_reason.is_empty() { - format!(": {}", stab.unstable_reason) - } else { - String::new() - }; - let text = format!("Unstable{}{}", unstable_extra, Markdown(&unstable_reason)); - stability.push(format!("
{}
", text)) + stability.push(format!("
Experimental
")) + } }; } else if let Some(depr) = item.deprecation.as_ref() { let note = if show_reason && !depr.note.is_empty() { @@ -1906,7 +1913,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec{}", text)) } @@ -2122,9 +2129,25 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,